--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/webengine/osswebengine/WebKit/WebView/WebView.mm Mon Mar 30 12:54:55 2009 +0300
@@ -0,0 +1,4576 @@
+/*
+ * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 David Smith (catfish.man@gmail.com)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "WebViewInternal.h"
+
+#import "DOMRangeInternal.h"
+#import "WebBackForwardList.h"
+#import "WebBackForwardListInternal.h"
+#import "WebBaseNetscapePluginView.h"
+#import "WebChromeClient.h"
+#import "WebContextMenuClient.h"
+#import "WebDOMOperationsPrivate.h"
+#import "WebDashboardRegion.h"
+#import "WebDataSourceInternal.h"
+#import "WebDefaultEditingDelegate.h"
+#import "WebDefaultPolicyDelegate.h"
+#import "WebDefaultScriptDebugDelegate.h"
+#import "WebDefaultUIDelegate.h"
+#import "WebDocument.h"
+#import "WebDocumentInternal.h"
+#import "WebDownload.h"
+#import "WebDownloadInternal.h"
+#import "WebDragClient.h"
+#import "WebDynamicScrollBarsView.h"
+#import "WebEditingDelegate.h"
+#import "WebEditorClient.h"
+#import "WebFormDelegatePrivate.h"
+#import "WebFrameBridge.h"
+#import "WebFrameInternal.h"
+#import "WebFrameViewInternal.h"
+#import "WebHTMLRepresentation.h"
+#import "WebHTMLViewInternal.h"
+#import "WebHistoryItemInternal.h"
+#import "WebIconDatabase.h"
+#import "WebIconDatabaseInternal.h"
+#import "WebInspectorClient.h"
+#import "WebKitErrors.h"
+#import "WebKitLogging.h"
+#import "WebKitNSStringExtras.h"
+#import "WebKitStatisticsPrivate.h"
+#import "WebKitSystemBits.h"
+#import "WebKitVersionChecks.h"
+#import "WebLocalizableStrings.h"
+#import "WebNSDataExtras.h"
+#import "WebNSDataExtrasPrivate.h"
+#import "WebNSDictionaryExtras.h"
+#import "WebNSEventExtras.h"
+#import "WebNSObjectExtras.h"
+#import "WebNSPasteboardExtras.h"
+#import "WebNSPrintOperationExtras.h"
+#import "WebNSURLExtras.h"
+#import "WebNSURLRequestExtras.h"
+#import "WebNSUserDefaultsExtras.h"
+#import "WebNSViewExtras.h"
+#import "WebPanelAuthenticationHandler.h"
+#import "WebPasteboardHelper.h"
+#import "WebPDFView.h"
+#import "WebPluginDatabase.h"
+#import "WebPolicyDelegate.h"
+#import "WebPreferenceKeysPrivate.h"
+#import "WebPreferencesPrivate.h"
+#import "WebScriptDebugDelegatePrivate.h"
+#import "WebScriptDebugServerPrivate.h"
+#import "WebUIDelegate.h"
+#import "WebUIDelegatePrivate.h"
+#import <CoreFoundation/CFSet.h>
+#import <Foundation/NSURLConnection.h>
+#import <JavaScriptCore/Assertions.h>
+#import <WebCore/Cache.h>
+#import <WebCore/ColorMac.h>
+#import <WebCore/Document.h>
+#import <WebCore/DocumentLoader.h>
+#import <WebCore/DragController.h>
+#import <WebCore/DragData.h>
+#import <WebCore/Editor.h>
+#import <WebCore/ExceptionHandlers.h>
+#import <WebCore/Frame.h>
+#import <WebCore/FrameLoader.h>
+#import <WebCore/FrameTree.h>
+#import <WebCore/HTMLNames.h>
+#import <WebCore/HistoryItem.h>
+#import <WebCore/Logging.h>
+#import <WebCore/MIMETypeRegistry.h>
+#import <WebCore/Page.h>
+#import <WebCore/PageCache.h>
+#import <WebCore/PlatformMouseEvent.h>
+#import <WebCore/ProgressTracker.h>
+#import <WebCore/SelectionController.h>
+#import <WebCore/Settings.h>
+#import <WebCore/TextResourceDecoder.h>
+#import <WebCore/WebCoreFrameBridge.h>
+#import <WebCore/WebCoreObjCExtras.h>
+#import <WebCore/WebCoreTextRenderer.h>
+#import <WebCore/WebCoreView.h>
+#import <WebKit/DOM.h>
+#import <WebKit/DOMExtensions.h>
+#import <WebKit/DOMPrivate.h>
+#import <WebKitSystemInterface.h>
+#import <mach-o/dyld.h>
+#import <objc/objc-runtime.h>
+#import <wtf/RefPtr.h>
+#import <wtf/HashTraits.h>
+
+using namespace WebCore;
+
+#if defined(__ppc__) || defined(__ppc64__)
+#define PROCESSOR "PPC"
+#elif defined(__i386__) || defined(__x86_64__)
+#define PROCESSOR "Intel"
+#else
+#error Unknown architecture
+#endif
+
+#define FOR_EACH_RESPONDER_SELECTOR(macro) \
+macro(alignCenter) \
+macro(alignJustified) \
+macro(alignLeft) \
+macro(alignRight) \
+macro(capitalizeWord) \
+macro(centerSelectionInVisibleArea) \
+macro(changeAttributes) \
+macro(changeColor) \
+macro(changeDocumentBackgroundColor) \
+macro(changeFont) \
+macro(checkSpelling) \
+macro(complete) \
+macro(copy) \
+macro(copyFont) \
+macro(cut) \
+macro(delete) \
+macro(deleteBackward) \
+macro(deleteBackwardByDecomposingPreviousCharacter) \
+macro(deleteForward) \
+macro(deleteToBeginningOfLine) \
+macro(deleteToBeginningOfParagraph) \
+macro(deleteToEndOfLine) \
+macro(deleteToEndOfParagraph) \
+macro(deleteWordBackward) \
+macro(deleteWordForward) \
+macro(ignoreSpelling) \
+macro(indent) \
+macro(insertBacktab) \
+macro(insertNewline) \
+macro(insertNewlineIgnoringFieldEditor) \
+macro(insertParagraphSeparator) \
+macro(insertTab) \
+macro(insertTabIgnoringFieldEditor) \
+macro(lowercaseWord) \
+macro(moveBackward) \
+macro(moveBackwardAndModifySelection) \
+macro(moveDown) \
+macro(moveDownAndModifySelection) \
+macro(moveForward) \
+macro(moveForwardAndModifySelection) \
+macro(moveLeft) \
+macro(moveLeftAndModifySelection) \
+macro(moveRight) \
+macro(moveRightAndModifySelection) \
+macro(moveToBeginningOfDocument) \
+macro(moveToBeginningOfDocumentAndModifySelection) \
+macro(moveToBeginningOfSentence) \
+macro(moveToBeginningOfSentenceAndModifySelection) \
+macro(moveToBeginningOfLine) \
+macro(moveToBeginningOfLineAndModifySelection) \
+macro(moveToBeginningOfParagraph) \
+macro(moveToBeginningOfParagraphAndModifySelection) \
+macro(moveToEndOfDocument) \
+macro(moveToEndOfDocumentAndModifySelection) \
+macro(moveToEndOfLine) \
+macro(moveToEndOfLineAndModifySelection) \
+macro(moveToEndOfParagraph) \
+macro(moveToEndOfParagraphAndModifySelection) \
+macro(moveToEndOfSentence) \
+macro(moveToEndOfSentenceAndModifySelection) \
+macro(moveUp) \
+macro(moveUpAndModifySelection) \
+macro(moveWordBackward) \
+macro(moveWordBackwardAndModifySelection) \
+macro(moveWordForward) \
+macro(moveWordForwardAndModifySelection) \
+macro(moveWordLeft) \
+macro(moveWordLeftAndModifySelection) \
+macro(moveWordRight) \
+macro(moveWordRightAndModifySelection) \
+macro(outdent) \
+macro(pageDown) \
+macro(pageUp) \
+macro(paste) \
+macro(pasteAsPlainText) \
+macro(pasteAsRichText) \
+macro(pasteFont) \
+macro(performFindPanelAction) \
+macro(scrollLineDown) \
+macro(scrollLineUp) \
+macro(scrollPageDown) \
+macro(scrollPageUp) \
+macro(scrollToBeginningOfDocument) \
+macro(scrollToEndOfDocument) \
+macro(selectAll) \
+macro(selectWord) \
+macro(selectSentence) \
+macro(selectLine) \
+macro(selectParagraph) \
+macro(showGuessPanel) \
+macro(startSpeaking) \
+macro(stopSpeaking) \
+macro(subscript) \
+macro(superscript) \
+macro(underline) \
+macro(unscript) \
+macro(uppercaseWord) \
+macro(yank) \
+macro(yankAndSelect) \
+
+#define WebKitOriginalTopPrintingMarginKey @"WebKitOriginalTopMargin"
+#define WebKitOriginalBottomPrintingMarginKey @"WebKitOriginalBottomMargin"
+
+static BOOL s_didSetCacheModel;
+static WebCacheModel s_cacheModel = WebCacheModelDocumentViewer;
+
+static BOOL applicationIsTerminating;
+static int pluginDatabaseClientCount = 0;
+
+@interface NSSpellChecker (AppKitSecretsIKnow)
+- (void)_preflightChosenSpellServer;
+@end
+
+@interface NSView (AppKitSecretsIKnow)
+- (NSView *)_hitTest:(NSPoint *)aPoint dragTypes:(NSSet *)types;
+- (void)_autoscrollForDraggingInfo:(id)dragInfo timeDelta:(NSTimeInterval)repeatDelta;
+- (BOOL)_shouldAutoscrollForDraggingInfo:(id)dragInfo;
+@end
+
+@interface NSWindow (AppKitSecretsIKnow)
+- (id)_oldFirstResponderBeforeBecoming;
+@end
+
+@interface NSObject (ValidateWithoutDelegate)
+- (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item;
+@end
+
+@interface _WebSafeForwarder : NSObject
+{
+ id target; // Non-retained. Don't retain delegates.
+ id defaultTarget;
+ BOOL catchExceptions;
+}
+- (id)initWithTarget:(id)target defaultTarget:(id)defaultTarget catchExceptions:(BOOL)catchExceptions;
+@end
+
+@interface WebViewPrivate : NSObject
+{
+@public
+ Page* page;
+
+ id UIDelegate;
+ id UIDelegateForwarder;
+ id resourceProgressDelegate;
+ id downloadDelegate;
+ id policyDelegate;
+ id policyDelegateForwarder;
+ id frameLoadDelegate;
+ id frameLoadDelegateForwarder;
+ id <WebFormDelegate> formDelegate;
+ id editingDelegate;
+ id editingDelegateForwarder;
+ id scriptDebugDelegate;
+ id scriptDebugDelegateForwarder;
+
+ BOOL allowsUndo;
+
+ float textSizeMultiplier;
+
+ NSString *applicationNameForUserAgent;
+ String* userAgent;
+ BOOL userAgentOverridden;
+
+ WebPreferences *preferences;
+ BOOL useSiteSpecificSpoofing;
+
+ NSWindow *hostWindow;
+
+ int programmaticFocusCount;
+
+ WebResourceDelegateImplementationCache resourceLoadDelegateImplementations;
+ WebFrameLoadDelegateImplementationCache frameLoadDelegateImplementations;
+
+ void *observationInfo;
+
+ BOOL closed;
+ BOOL shouldCloseWithWindow;
+ BOOL mainFrameDocumentReady;
+ BOOL drawsBackground;
+ BOOL editable;
+ BOOL tabKeyCyclesThroughElementsChanged;
+ BOOL becomingFirstResponder;
+ BOOL becomingFirstResponderFromOutside;
+ BOOL hoverFeedbackSuspended;
+ BOOL usesPageCache;
+ BOOL catchesDelegateExceptions;
+
+ NSColor *backgroundColor;
+
+ NSString *mediaStyle;
+
+ BOOL hasSpellCheckerDocumentTag;
+ NSInteger spellCheckerDocumentTag;
+
+ BOOL smartInsertDeleteEnabled;
+
+ BOOL dashboardBehaviorAlwaysSendMouseEventsToAllWindows;
+ BOOL dashboardBehaviorAlwaysSendActiveNullEventsToPlugIns;
+ BOOL dashboardBehaviorAlwaysAcceptsFirstMouse;
+ BOOL dashboardBehaviorAllowWheelScrolling;
+
+ // WebKit has both a global plug-in database and a separate, per WebView plug-in database. Dashboard uses the per WebView database.
+ WebPluginDatabase *pluginDatabase;
+
+ HashMap<unsigned long, RetainPtr<id> >* identifierMap;
+}
+@end
+
+@interface WebView (WebFileInternal)
++ (void)_setCacheModel:(WebCacheModel)cacheModel;
++ (WebCacheModel)_cacheModel;
+- (WebFrame *)_selectedOrMainFrame;
+- (WebFrameBridge *)_bridgeForSelectedOrMainFrame;
+- (BOOL)_isLoading;
+- (WebFrameView *)_frameViewAtWindowPoint:(NSPoint)point;
+- (WebFrame *)_focusedFrame;
++ (void)_preflightSpellChecker;
+- (BOOL)_continuousCheckingAllowed;
+- (NSResponder *)_responderForResponderOperations;
+- (BOOL)_performTextSizingSelector:(SEL)sel withObject:(id)arg onTrackingDocs:(BOOL)doTrackingViews selForNonTrackingDocs:(SEL)testSel newScaleFactor:(float)newScaleFactor;
+- (void)_notifyTextSizeMultiplierChanged;
+@end
+
+@interface WebView (WebCallDelegateFunctions)
+@end
+
+NSString *WebElementDOMNodeKey = @"WebElementDOMNode";
+NSString *WebElementFrameKey = @"WebElementFrame";
+NSString *WebElementImageKey = @"WebElementImage";
+NSString *WebElementImageAltStringKey = @"WebElementImageAltString";
+NSString *WebElementImageRectKey = @"WebElementImageRect";
+NSString *WebElementImageURLKey = @"WebElementImageURL";
+NSString *WebElementIsSelectedKey = @"WebElementIsSelected";
+NSString *WebElementLinkLabelKey = @"WebElementLinkLabel";
+NSString *WebElementLinkTargetFrameKey = @"WebElementTargetFrame";
+NSString *WebElementLinkTitleKey = @"WebElementLinkTitle";
+NSString *WebElementLinkURLKey = @"WebElementLinkURL";
+NSString *WebElementSpellingToolTipKey = @"WebElementSpellingToolTip";
+NSString *WebElementTitleKey = @"WebElementTitle";
+NSString *WebElementLinkIsLiveKey = @"WebElementLinkIsLive";
+NSString *WebElementIsContentEditableKey = @"WebElementIsContentEditableKey";
+
+NSString *WebViewProgressStartedNotification = @"WebProgressStartedNotification";
+NSString *WebViewProgressEstimateChangedNotification = @"WebProgressEstimateChangedNotification";
+NSString *WebViewProgressFinishedNotification = @"WebProgressFinishedNotification";
+
+NSString * const WebViewDidBeginEditingNotification = @"WebViewDidBeginEditingNotification";
+NSString * const WebViewDidChangeNotification = @"WebViewDidChangeNotification";
+NSString * const WebViewDidEndEditingNotification = @"WebViewDidEndEditingNotification";
+NSString * const WebViewDidChangeTypingStyleNotification = @"WebViewDidChangeTypingStyleNotification";
+NSString * const WebViewDidChangeSelectionNotification = @"WebViewDidChangeSelectionNotification";
+
+enum { WebViewVersion = 4 };
+
+#define timedLayoutSize 4096
+
+static NSMutableSet *schemesWithRepresentationsSet;
+
+NSString *_WebCanGoBackKey = @"canGoBack";
+NSString *_WebCanGoForwardKey = @"canGoForward";
+NSString *_WebEstimatedProgressKey = @"estimatedProgress";
+NSString *_WebIsLoadingKey = @"isLoading";
+NSString *_WebMainFrameIconKey = @"mainFrameIcon";
+NSString *_WebMainFrameTitleKey = @"mainFrameTitle";
+NSString *_WebMainFrameURLKey = @"mainFrameURL";
+NSString *_WebMainFrameDocumentKey = @"mainFrameDocument";
+
+@interface WebProgressItem : NSObject
+{
+@public
+ long long bytesReceived;
+ long long estimatedLength;
+}
+@end
+
+@implementation WebProgressItem
+@end
+
+static BOOL continuousSpellCheckingEnabled;
+#ifndef BUILDING_ON_TIGER
+static BOOL grammarCheckingEnabled;
+#endif
+
+@implementation WebViewPrivate
+
+#ifndef BUILDING_ON_TIGER
++ (void)initialize
+{
+ WebCoreObjCFinalizeOnMainThread(self);
+}
+#endif
+
+- init
+{
+ self = [super init];
+ if (!self)
+ return nil;
+ allowsUndo = YES;
+ textSizeMultiplier = 1;
+ dashboardBehaviorAllowWheelScrolling = YES;
+ shouldCloseWithWindow = objc_collecting_enabled();
+ continuousSpellCheckingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebContinuousSpellCheckingEnabled];
+
+#ifndef BUILDING_ON_TIGER
+ grammarCheckingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebGrammarCheckingEnabled];
+#endif
+ userAgent = new String;
+
+ usesPageCache = YES;
+
+ identifierMap = new HashMap<unsigned long, RetainPtr<id> >();
+ pluginDatabaseClientCount++;
+
+ return self;
+}
+
+- (void)dealloc
+{
+ ASSERT(!page);
+ ASSERT(!preferences);
+
+ delete userAgent;
+ delete identifierMap;
+
+ [applicationNameForUserAgent release];
+ [backgroundColor release];
+
+ [hostWindow release];
+
+ [policyDelegateForwarder release];
+ [UIDelegateForwarder release];
+ [frameLoadDelegateForwarder release];
+ [editingDelegateForwarder release];
+ [scriptDebugDelegateForwarder release];
+
+ [mediaStyle release];
+
+ [super dealloc];
+}
+
+- (void)finalize
+{
+ ASSERT_MAIN_THREAD();
+
+ delete userAgent;
+ delete identifierMap;
+
+ [super finalize];
+}
+
+@end
+
+@implementation WebView (AllWebViews)
+
+static CFSetCallBacks NonRetainingSetCallbacks = {
+ 0,
+ NULL,
+ NULL,
+ CFCopyDescription,
+ CFEqual,
+ CFHash
+};
+
+static CFMutableSetRef allWebViewsSet;
+
++ (void)_makeAllWebViewsPerformSelector:(SEL)selector
+{
+ if (!allWebViewsSet)
+ return;
+
+ [(NSMutableSet *)allWebViewsSet makeObjectsPerformSelector:selector];
+}
+
+- (void)_removeFromAllWebViewsSet
+{
+ if (allWebViewsSet)
+ CFSetRemoveValue(allWebViewsSet, self);
+}
+
+- (void)_addToAllWebViewsSet
+{
+ if (!allWebViewsSet)
+ allWebViewsSet = CFSetCreateMutable(NULL, 0, &NonRetainingSetCallbacks);
+
+ CFSetSetValue(allWebViewsSet, self);
+}
+
+@end
+
+@implementation WebView (WebPrivate)
+
+#ifdef DEBUG_WIDGET_DRAWING
+static bool debugWidget = true;
+- (void)drawRect:(NSRect)rect
+{
+ [[NSColor blueColor] set];
+ NSRectFill (rect);
+
+ NSRect htmlViewRect = [[[[self mainFrame] frameView] documentView] frame];
+
+ if (debugWidget) {
+ while (debugWidget) {
+ sleep (1);
+ }
+ }
+
+ NSLog (@"%s: rect: (%0.f,%0.f) %0.f %0.f, htmlViewRect: (%0.f,%0.f) %0.f %0.f\n",
+ __PRETTY_FUNCTION__, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height,
+ htmlViewRect.origin.x, htmlViewRect.origin.y, htmlViewRect.size.width, htmlViewRect.size.height
+ );
+
+ [super drawRect:rect];
+}
+#endif
+
++ (BOOL)_developerExtrasEnabled
+{
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ if ([defaults boolForKey:@"DisableWebKitDeveloperExtras"])
+ return NO;
+#ifdef NDEBUG
+ BOOL enableDebugger = [defaults boolForKey:@"WebKitDeveloperExtras"];
+ if (!enableDebugger)
+ enableDebugger = [defaults boolForKey:@"IncludeDebugMenu"];
+ return enableDebugger;
+#else
+ return YES; // always enable in debug builds
+#endif
+}
+
++ (BOOL)_scriptDebuggerEnabled
+{
+#ifdef NDEBUG
+ return [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitScriptDebuggerEnabled"];
+#else
+ return YES; // always enable in debug builds
+#endif
+}
+
++ (NSArray *)_supportedMIMETypes
+{
+ // Load the plug-in DB allowing plug-ins to install types.
+ [WebPluginDatabase sharedDatabase];
+ return [[WebFrameView _viewTypesAllowImageTypeOmission:NO] allKeys];
+}
+
++ (NSArray *)_supportedFileExtensions
+{
+ NSMutableSet *extensions = [[NSMutableSet alloc] init];
+ NSArray *MIMETypes = [self _supportedMIMETypes];
+ NSEnumerator *enumerator = [MIMETypes objectEnumerator];
+ NSString *MIMEType;
+ while ((MIMEType = [enumerator nextObject]) != nil) {
+ NSArray *extensionsForType = WKGetExtensionsForMIMEType(MIMEType);
+ if (extensionsForType) {
+ [extensions addObjectsFromArray:extensionsForType];
+ }
+ }
+ NSArray *uniqueExtensions = [extensions allObjects];
+ [extensions release];
+ return uniqueExtensions;
+}
+
++ (BOOL)_viewClass:(Class *)vClass andRepresentationClass:(Class *)rClass forMIMEType:(NSString *)MIMEType;
+{
+ MIMEType = [MIMEType lowercaseString];
+ Class viewClass = [[WebFrameView _viewTypesAllowImageTypeOmission:YES] _webkit_objectForMIMEType:MIMEType];
+ Class repClass = [[WebDataSource _repTypesAllowImageTypeOmission:YES] _webkit_objectForMIMEType:MIMEType];
+
+ if (!viewClass || !repClass || [[WebPDFView supportedMIMETypes] containsObject:MIMEType]) {
+ // Our optimization to avoid loading the plug-in DB and image types for the HTML case failed.
+ // Load the plug-in DB allowing plug-ins to install types.
+ [WebPluginDatabase sharedDatabase];
+
+ // Load the image types and get the view class and rep class. This should be the fullest picture of all handled types.
+ viewClass = [[WebFrameView _viewTypesAllowImageTypeOmission:NO] _webkit_objectForMIMEType:MIMEType];
+ repClass = [[WebDataSource _repTypesAllowImageTypeOmission:NO] _webkit_objectForMIMEType:MIMEType];
+ }
+
+ if (viewClass && repClass) {
+ // Special-case WebHTMLView for text types that shouldn't be shown.
+ if (viewClass == [WebHTMLView class] &&
+ repClass == [WebHTMLRepresentation class] &&
+ [[WebHTMLView unsupportedTextMIMETypes] containsObject:MIMEType]) {
+ return NO;
+ }
+ if (vClass)
+ *vClass = viewClass;
+ if (rClass)
+ *rClass = repClass;
+ return YES;
+ }
+
+ return NO;
+}
+
+- (BOOL)_viewClass:(Class *)vClass andRepresentationClass:(Class *)rClass forMIMEType:(NSString *)MIMEType;
+{
+ if ([[self class] _viewClass:vClass andRepresentationClass:rClass forMIMEType:MIMEType])
+ return YES;
+
+ if (_private->pluginDatabase) {
+ WebBasePluginPackage *pluginPackage = [_private->pluginDatabase pluginForMIMEType:MIMEType];
+ if (pluginPackage) {
+ if (vClass)
+ *vClass = [WebHTMLView class];
+ if (rClass)
+ *rClass = [WebHTMLRepresentation class];
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
++ (void)_setAlwaysUseATSU:(BOOL)f
+{
+ WebCoreSetAlwaysUseATSU(f);
+}
+
++ (BOOL)canShowFile:(NSString *)path
+{
+ return [[self class] canShowMIMEType:[WebView _MIMETypeForFile:path]];
+}
+
++ (NSString *)suggestedFileExtensionForMIMEType:(NSString *)type
+{
+ return WKGetPreferredExtensionForMIMEType(type);
+}
+
+- (BOOL)_isClosed
+{
+ if (!_private || _private->closed)
+ return YES;
+ return NO;
+}
+
+- (void)_close
+{
+ if (!_private || _private->closed)
+ return;
+
+ FrameLoader* mainFrameLoader = [[self mainFrame] _frameLoader];
+ if (mainFrameLoader)
+ mainFrameLoader->detachFromParent();
+
+ [self _removeFromAllWebViewsSet];
+ [self setGroupName:nil];
+ [self setHostWindow:nil];
+
+ [self setDownloadDelegate:nil];
+ [self setEditingDelegate:nil];
+ [self setFrameLoadDelegate:nil];
+ [self setPolicyDelegate:nil];
+ [self setResourceLoadDelegate:nil];
+ [self setScriptDebugDelegate:nil];
+ [self setUIDelegate:nil];
+
+ // setHostWindow:nil must be called before this value is set (see 5408186)
+ _private->closed = YES;
+
+ // To avoid leaks, call removeDragCaret in case it wasn't called after moveDragCaretToPoint.
+ [self removeDragCaret];
+
+ // Deleteing the WebCore::Page will clear the page cache so we call destroy on
+ // all the plug-ins in the page cache to break any retain cycles.
+ // See comment in HistoryItem::releaseAllPendingPageCaches() for more information.
+ delete _private->page;
+ _private->page = 0;
+
+ if (_private->hasSpellCheckerDocumentTag) {
+ [[NSSpellChecker sharedSpellChecker] closeSpellDocumentWithTag:_private->spellCheckerDocumentTag];
+ _private->hasSpellCheckerDocumentTag = NO;
+ }
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ [WebPreferences _removeReferenceForIdentifier:[self preferencesIdentifier]];
+
+ WebPreferences *preferences = _private->preferences;
+ _private->preferences = nil;
+ [preferences didRemoveFromWebView];
+ [preferences release];
+
+ pluginDatabaseClientCount--;
+
+ // Make sure to close both sets of plug-ins databases because plug-ins need an opportunity to clean up files, etc.
+
+ // Unload the WebView local plug-in database.
+ if (_private->pluginDatabase) {
+ [_private->pluginDatabase close];
+ [_private->pluginDatabase release];
+ _private->pluginDatabase = nil;
+ }
+
+ // Keep the global plug-in database active until the app terminates to avoid having to reload plug-in bundles.
+ if (!pluginDatabaseClientCount && applicationIsTerminating)
+ [WebPluginDatabase closeSharedDatabase];
+}
+
++ (NSString *)_MIMETypeForFile:(NSString *)path
+{
+ NSString *extension = [path pathExtension];
+ NSString *MIMEType = nil;
+
+ // Get the MIME type from the extension.
+ if ([extension length] != 0) {
+ MIMEType = WKGetMIMETypeForExtension(extension);
+ }
+
+ // If we can't get a known MIME type from the extension, sniff.
+ if ([MIMEType length] == 0 || [MIMEType isEqualToString:@"application/octet-stream"]) {
+ NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:path];
+ NSData *data = [handle readDataOfLength:WEB_GUESS_MIME_TYPE_PEEK_LENGTH];
+ [handle closeFile];
+ if ([data length] != 0) {
+ MIMEType = [data _webkit_guessedMIMEType];
+ }
+ if ([MIMEType length] == 0) {
+ MIMEType = @"application/octet-stream";
+ }
+ }
+
+ return MIMEType;
+}
+
+- (WebDownload *)_downloadURL:(NSURL *)URL
+{
+ ASSERT(URL);
+
+ NSURLRequest *request = [[NSURLRequest alloc] initWithURL:URL];
+ WebDownload *download = [WebDownload _downloadWithRequest:request
+ delegate:_private->downloadDelegate
+ directory:nil];
+ [request release];
+
+ return download;
+}
+
+- (WebView *)_openNewWindowWithRequest:(NSURLRequest *)request
+{
+ WebView *newWindowWebView = CallUIDelegate(self, @selector(webView:createWebViewWithRequest:), request);
+ if (!newWindowWebView)
+ return nil;
+
+ CallUIDelegate(newWindowWebView, @selector(webViewShow:));
+ return newWindowWebView;
+}
+
+- (WebCore::Page*)page
+{
+ return _private->page;
+}
+
+- (NSMenu *)_menuForElement:(NSDictionary *)element defaultItems:(NSArray *)items
+{
+ NSArray *defaultMenuItems = [[WebDefaultUIDelegate sharedUIDelegate] webView:self contextMenuItemsForElement:element defaultMenuItems:items];
+
+ NSArray *menuItems = CallUIDelegate(self, @selector(webView:contextMenuItemsForElement:defaultMenuItems:), element, defaultMenuItems);
+ if (!menuItems)
+ return nil;
+
+ unsigned count = [menuItems count];
+ if (!count)
+ return nil;
+
+ NSMenu *menu = [[NSMenu alloc] init];
+ for (unsigned i = 0; i < count; i++)
+ [menu addItem:[menuItems objectAtIndex:i]];
+
+ return [menu autorelease];
+}
+
+- (void)_mouseDidMoveOverElement:(NSDictionary *)dictionary modifierFlags:(NSUInteger)modifierFlags
+{
+ // We originally intended to call this delegate method sometimes with a nil dictionary, but due to
+ // a bug dating back to WebKit 1.0 this delegate was never called with nil! Unfortunately we can't
+ // start calling this with nil since it will break Adobe Help Viewer, and possibly other clients.
+ if (!dictionary)
+ return;
+ CallUIDelegate(self, @selector(webView:mouseDidMoveOverElement:modifierFlags:), dictionary, modifierFlags);
+}
+
+- (void)_loadBackForwardListFromOtherView:(WebView *)otherView
+{
+ if (!_private->page)
+ return;
+
+ if (!otherView->_private->page)
+ return;
+
+ // It turns out the right combination of behavior is done with the back/forward load
+ // type. (See behavior matrix at the top of WebFramePrivate.) So we copy all the items
+ // in the back forward list, and go to the current one.
+
+ BackForwardList* backForwardList = _private->page->backForwardList();
+ ASSERT(!backForwardList->currentItem()); // destination list should be empty
+
+ BackForwardList* otherBackForwardList = otherView->_private->page->backForwardList();
+ if (!otherBackForwardList->currentItem())
+ return; // empty back forward list, bail
+
+ HistoryItem* newItemToGoTo = 0;
+
+ int lastItemIndex = otherBackForwardList->forwardListCount();
+ for (int i = -otherBackForwardList->backListCount(); i <= lastItemIndex; ++i) {
+ if (i == 0) {
+ // If this item is showing , save away its current scroll and form state,
+ // since that might have changed since loading and it is normally not saved
+ // until we leave that page.
+ otherView->_private->page->mainFrame()->loader()->saveDocumentAndScrollState();
+ }
+ RefPtr<HistoryItem> newItem = otherBackForwardList->itemAtIndex(i)->copy();
+ if (i == 0)
+ newItemToGoTo = newItem.get();
+ backForwardList->addItem(newItem.release());
+ }
+
+ ASSERT(newItemToGoTo);
+ _private->page->goToItem(newItemToGoTo, FrameLoadTypeIndexedBackForward);
+}
+
+- (void)_setFormDelegate: (id<WebFormDelegate>)delegate
+{
+ _private->formDelegate = delegate;
+}
+
+- (id<WebFormDelegate>)_formDelegate
+{
+ return _private->formDelegate;
+}
+
+- (BOOL)_needsAdobeFrameReloadingQuirk
+{
+ static BOOL checked = NO;
+ static BOOL needsQuirk = NO;
+
+ if (checked)
+ return needsQuirk;
+
+ needsQuirk = WKAppVersionCheckLessThan(@"com.adobe.Acrobat", -1, 9.0)
+ || WKAppVersionCheckLessThan(@"com.adobe.Acrobat.Pro", -1, 9.0)
+ || WKAppVersionCheckLessThan(@"com.adobe.Reader", -1, 9.0)
+ || WKAppVersionCheckLessThan(@"com.adobe.distiller", -1, 9.0)
+ || WKAppVersionCheckLessThan(@"com.adobe.Contribute", -1, 4.2)
+ || WKAppVersionCheckLessThan(@"com.adobe.dreamweaver-9.0", -1, 9.1)
+ || WKAppVersionCheckLessThan(@"com.macromedia.fireworks", -1, 9.1)
+ || WKAppVersionCheckLessThan(@"com.adobe.InCopy", -1, 5.1)
+ || WKAppVersionCheckLessThan(@"com.adobe.InDesign", -1, 5.1)
+ || WKAppVersionCheckLessThan(@"com.adobe.Soundbooth", -1, 2);
+
+ return needsQuirk;
+}
+
+- (void)_preferencesChangedNotification:(NSNotification *)notification
+{
+ WebPreferences *preferences = (WebPreferences *)[notification object];
+ ASSERT(preferences == [self preferences]);
+
+ if (!_private->userAgentOverridden)
+ *_private->userAgent = String();
+
+ // Cache this value so we don't have to read NSUserDefaults on each page load
+ _private->useSiteSpecificSpoofing = [preferences _useSiteSpecificSpoofing];
+
+ // Update corresponding WebCore Settings object.
+ if (!_private->page)
+ return;
+
+ Settings* settings = _private->page->settings();
+
+ settings->setCursiveFontFamily([preferences cursiveFontFamily]);
+ settings->setDefaultFixedFontSize([preferences defaultFixedFontSize]);
+ settings->setDefaultFontSize([preferences defaultFontSize]);
+ settings->setDefaultTextEncodingName([preferences defaultTextEncodingName]);
+ settings->setFantasyFontFamily([preferences fantasyFontFamily]);
+ settings->setFixedFontFamily([preferences fixedFontFamily]);
+ settings->setForceFTPDirectoryListings([preferences _forceFTPDirectoryListings]);
+ settings->setFTPDirectoryTemplatePath([preferences _ftpDirectoryTemplatePath]);
+ settings->setJavaEnabled([preferences isJavaEnabled]);
+ settings->setJavaScriptEnabled([preferences isJavaScriptEnabled]);
+ settings->setJavaScriptCanOpenWindowsAutomatically([preferences javaScriptCanOpenWindowsAutomatically]);
+ settings->setMinimumFontSize([preferences minimumFontSize]);
+ settings->setMinimumLogicalFontSize([preferences minimumLogicalFontSize]);
+ settings->setPluginsEnabled([preferences arePlugInsEnabled]);
+ settings->setPrivateBrowsingEnabled([preferences privateBrowsingEnabled]);
+ settings->setSansSerifFontFamily([preferences sansSerifFontFamily]);
+ settings->setSerifFontFamily([preferences serifFontFamily]);
+ settings->setStandardFontFamily([preferences standardFontFamily]);
+ settings->setLoadsImagesAutomatically([preferences loadsImagesAutomatically]);
+ settings->setShouldPrintBackgrounds([preferences shouldPrintBackgrounds]);
+ settings->setTextAreasAreResizable([preferences textAreasAreResizable]);
+ settings->setShrinksStandaloneImagesToFit([preferences shrinksStandaloneImagesToFit]);
+ settings->setEditableLinkBehavior(core([preferences editableLinkBehavior]));
+ settings->setDOMPasteAllowed([preferences isDOMPasteAllowed]);
+ settings->setUsesPageCache([self usesPageCache]);
+ settings->setShowsURLsInToolTips([preferences showsURLsInToolTips]);
+ settings->setDeveloperExtrasEnabled([WebView _developerExtrasEnabled]);
+ if ([preferences userStyleSheetEnabled]) {
+ NSString* location = [[preferences userStyleSheetLocation] _web_originalDataAsString];
+ settings->setUserStyleSheetLocation([NSURL URLWithString:(location ? location : @"")]);
+ } else
+ settings->setUserStyleSheetLocation([NSURL URLWithString:@""]);
+ settings->setNeedsAdobeFrameReloadingQuirk([self _needsAdobeFrameReloadingQuirk]);
+}
+
+static inline IMP getMethod(id o, SEL s)
+{
+ return [o respondsToSelector:s] ? [o methodForSelector:s] : 0;
+}
+
+- (void)_cacheResourceLoadDelegateImplementations
+{
+ WebResourceDelegateImplementationCache *cache = &_private->resourceLoadDelegateImplementations;
+ id delegate = _private->resourceProgressDelegate;
+
+ cache->didCancelAuthenticationChallengeFunc = getMethod(delegate, @selector(webView:resource:didReceiveAuthenticationChallenge:fromDataSource:));
+ cache->didFailLoadingWithErrorFromDataSourceFunc = getMethod(delegate, @selector(webView:resource:didFailLoadingWithError:fromDataSource:));
+ cache->didFinishLoadingFromDataSourceFunc = getMethod(delegate, @selector(webView:resource:didFinishLoadingFromDataSource:));
+ cache->didLoadResourceFromMemoryCacheFunc = getMethod(delegate, @selector(webView:didLoadResourceFromMemoryCache:response:length:fromDataSource:));
+ cache->didReceiveAuthenticationChallengeFunc = getMethod(delegate, @selector(webView:resource:didReceiveAuthenticationChallenge:fromDataSource:));
+ cache->didReceiveContentLengthFunc = getMethod(delegate, @selector(webView:resource:didReceiveContentLength:fromDataSource:));
+ cache->didReceiveResponseFunc = getMethod(delegate, @selector(webView:resource:didReceiveResponse:fromDataSource:));
+ cache->identifierForRequestFunc = getMethod(delegate, @selector(webView:identifierForInitialRequest:fromDataSource:));
+ cache->plugInFailedWithErrorFunc = getMethod(delegate, @selector(webView:plugInFailedWithError:dataSource:));
+ cache->willCacheResponseFunc = getMethod(delegate, @selector(webView:resource:willCacheResponse:fromDataSource:));
+ cache->willSendRequestFunc = getMethod(delegate, @selector(webView:resource:willSendRequest:redirectResponse:fromDataSource:));
+}
+
+WebResourceDelegateImplementationCache WebViewGetResourceLoadDelegateImplementations(WebView *webView)
+{
+ return webView->_private->resourceLoadDelegateImplementations;
+}
+
+- (void)_cacheFrameLoadDelegateImplementations
+{
+ WebFrameLoadDelegateImplementationCache *cache = &_private->frameLoadDelegateImplementations;
+ id delegate = _private->frameLoadDelegate;
+
+ cache->didCancelClientRedirectForFrameFunc = getMethod(delegate, @selector(webView:didCancelClientRedirectForFrame:));
+ cache->didChangeLocationWithinPageForFrameFunc = getMethod(delegate, @selector(webView:didChangeLocationWithinPageForFrame:));
+ cache->didClearWindowObjectForFrameFunc = getMethod(delegate, @selector(webView:didClearWindowObject:forFrame:));
+ cache->didCommitLoadForFrameFunc = getMethod(delegate, @selector(webView:didCommitLoadForFrame:));
+ cache->didFailLoadWithErrorForFrameFunc = getMethod(delegate, @selector(webView:didFailLoadWithError:forFrame:));
+ cache->didFailProvisionalLoadWithErrorForFrameFunc = getMethod(delegate, @selector(webView:didFailProvisionalLoadWithError:forFrame:));
+ cache->didFinishDocumentLoadForFrameFunc = getMethod(delegate, @selector(webView:didFinishDocumentLoadForFrame:));
+ cache->didFinishLoadForFrameFunc = getMethod(delegate, @selector(webView:didFinishLoadForFrame:));
+ cache->didFirstLayoutInFrameFunc = getMethod(delegate, @selector(webView:didFirstLayoutInFrame:));
+ cache->didHandleOnloadEventsForFrameFunc = getMethod(delegate, @selector(webView:didHandleOnloadEventsForFrame:));
+ cache->didReceiveIconForFrameFunc = getMethod(delegate, @selector(webView:didReceiveIcon:forFrame:));
+ cache->didReceiveServerRedirectForProvisionalLoadForFrameFunc = getMethod(delegate, @selector(webView:didReceiveServerRedirectForProvisionalLoadForFrame:));
+ cache->didReceiveTitleForFrameFunc = getMethod(delegate, @selector(webView:didReceiveTitle:forFrame:));
+ cache->didStartProvisionalLoadForFrameFunc = getMethod(delegate, @selector(webView:didStartProvisionalLoadForFrame:));
+ cache->willCloseFrameFunc = getMethod(delegate, @selector(webView:willCloseFrame:));
+ cache->willPerformClientRedirectToURLDelayFireDateForFrameFunc = getMethod(delegate, @selector(webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:));
+ cache->windowScriptObjectAvailableFunc = getMethod(delegate, @selector(webView:windowScriptObjectAvailable:));
+}
+
+WebFrameLoadDelegateImplementationCache WebViewGetFrameLoadDelegateImplementations(WebView *webView)
+{
+ return webView->_private->frameLoadDelegateImplementations;
+}
+
+- (id)_policyDelegateForwarder
+{
+ if (!_private->policyDelegateForwarder)
+ _private->policyDelegateForwarder = [[_WebSafeForwarder alloc] initWithTarget:_private->policyDelegate defaultTarget:[WebDefaultPolicyDelegate sharedPolicyDelegate] catchExceptions:_private->catchesDelegateExceptions];
+ return _private->policyDelegateForwarder;
+}
+
+- (id)_UIDelegateForwarder
+{
+ if (!_private->UIDelegateForwarder)
+ _private->UIDelegateForwarder = [[_WebSafeForwarder alloc] initWithTarget:_private->UIDelegate defaultTarget:[WebDefaultUIDelegate sharedUIDelegate] catchExceptions:_private->catchesDelegateExceptions];
+ return _private->UIDelegateForwarder;
+}
+
+- (id)_editingDelegateForwarder
+{
+ // This can be called during window deallocation by QTMovieView in the QuickTime Cocoa Plug-in.
+ // Not sure if that is a bug or not.
+ if (!_private)
+ return nil;
+
+ if (!_private->editingDelegateForwarder)
+ _private->editingDelegateForwarder = [[_WebSafeForwarder alloc] initWithTarget:_private->editingDelegate defaultTarget:[WebDefaultEditingDelegate sharedEditingDelegate] catchExceptions:_private->catchesDelegateExceptions];
+ return _private->editingDelegateForwarder;
+}
+
+- (id)_scriptDebugDelegateForwarder
+{
+ if (!_private->scriptDebugDelegateForwarder)
+ _private->scriptDebugDelegateForwarder = [[_WebSafeForwarder alloc] initWithTarget:_private->scriptDebugDelegate defaultTarget:[WebDefaultScriptDebugDelegate sharedScriptDebugDelegate] catchExceptions:_private->catchesDelegateExceptions];
+ return _private->scriptDebugDelegateForwarder;
+}
+
+- (void)_closeWindow
+{
+ [[self _UIDelegateForwarder] webViewClose:self];
+}
+
++ (void)_unregisterViewClassAndRepresentationClassForMIMEType:(NSString *)MIMEType;
+{
+ [[WebFrameView _viewTypesAllowImageTypeOmission:NO] removeObjectForKey:MIMEType];
+ [[WebDataSource _repTypesAllowImageTypeOmission:NO] removeObjectForKey:MIMEType];
+
+ // FIXME: We also need to maintain MIMEType registrations (which can be dynamically changed)
+ // in the WebCore MIMEType registry. For now we're doing this in a safe, limited manner
+ // to fix <rdar://problem/5372989> - a future revamping of the entire system is neccesary for future robustness
+ MIMETypeRegistry::getSupportedNonImageMIMETypes().remove(MIMEType);
+}
+
++ (void)_registerViewClass:(Class)viewClass representationClass:(Class)representationClass forURLScheme:(NSString *)URLScheme;
+{
+ NSString *MIMEType = [self _generatedMIMETypeForURLScheme:URLScheme];
+ [self registerViewClass:viewClass representationClass:representationClass forMIMEType:MIMEType];
+
+ // FIXME: We also need to maintain MIMEType registrations (which can be dynamically changed)
+ // in the WebCore MIMEType registry. For now we're doing this in a safe, limited manner
+ // to fix <rdar://problem/5372989> - a future revamping of the entire system is neccesary for future robustness
+ if ([viewClass class] == [WebHTMLView class])
+ MIMETypeRegistry::getSupportedNonImageMIMETypes().add(MIMEType);
+
+ // This is used to make _representationExistsForURLScheme faster.
+ // Without this set, we'd have to create the MIME type each time.
+ if (schemesWithRepresentationsSet == nil) {
+ schemesWithRepresentationsSet = [[NSMutableSet alloc] init];
+ }
+ [schemesWithRepresentationsSet addObject:[[[URLScheme lowercaseString] copy] autorelease]];
+}
+
++ (NSString *)_generatedMIMETypeForURLScheme:(NSString *)URLScheme
+{
+ return [@"x-apple-web-kit/" stringByAppendingString:[URLScheme lowercaseString]];
+}
+
++ (BOOL)_representationExistsForURLScheme:(NSString *)URLScheme
+{
+ return [schemesWithRepresentationsSet containsObject:[URLScheme lowercaseString]];
+}
+
++ (BOOL)_canHandleRequest:(NSURLRequest *)request
+{
+ // FIXME: If <rdar://problem/5217309> gets fixed, this check can be removed
+ if (!request)
+ return NO;
+
+ if ([NSURLConnection canHandleRequest:request])
+ return YES;
+
+ NSString *scheme = [[request URL] scheme];
+
+ if ([self _representationExistsForURLScheme:scheme])
+ return YES;
+
+ return ([scheme _webkit_isCaseInsensitiveEqualToString:@"applewebdata"]);
+}
+
++ (NSString *)_decodeData:(NSData *)data
+{
+ HTMLNames::init(); // this method is used for importing bookmarks at startup, so HTMLNames are likely to be uninitialized yet
+ RefPtr<TextResourceDecoder> decoder = new TextResourceDecoder("text/html"); // bookmark files are HTML
+ String result = decoder->decode(static_cast<const char*>([data bytes]), [data length]);
+ result += decoder->flush();
+ return result;
+}
+
+- (void)_pushPerformingProgrammaticFocus
+{
+ _private->programmaticFocusCount++;
+}
+
+- (void)_popPerformingProgrammaticFocus
+{
+ _private->programmaticFocusCount--;
+}
+
+- (BOOL)_isPerformingProgrammaticFocus
+{
+ return _private->programmaticFocusCount != 0;
+}
+
+- (void)_didChangeValueForKey: (NSString *)key
+{
+ LOG (Bindings, "calling didChangeValueForKey: %@", key);
+ [self didChangeValueForKey: key];
+}
+
+- (void)_willChangeValueForKey: (NSString *)key
+{
+ LOG (Bindings, "calling willChangeValueForKey: %@", key);
+ [self willChangeValueForKey: key];
+}
+
++ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
+ static NSSet *manualNotifyKeys = nil;
+ if (!manualNotifyKeys)
+ manualNotifyKeys = [[NSSet alloc] initWithObjects:_WebMainFrameURLKey, _WebIsLoadingKey, _WebEstimatedProgressKey,
+ _WebCanGoBackKey, _WebCanGoForwardKey, _WebMainFrameTitleKey, _WebMainFrameIconKey, _WebMainFrameDocumentKey, nil];
+ if ([manualNotifyKeys containsObject:key])
+ return NO;
+ return YES;
+}
+
+- (NSArray *)_declaredKeys {
+ static NSArray *declaredKeys = nil;
+ if (!declaredKeys)
+ declaredKeys = [[NSArray alloc] initWithObjects:_WebMainFrameURLKey, _WebIsLoadingKey, _WebEstimatedProgressKey,
+ _WebCanGoBackKey, _WebCanGoForwardKey, _WebMainFrameTitleKey, _WebMainFrameIconKey, _WebMainFrameDocumentKey, nil];
+ return declaredKeys;
+}
+
+- (void)setObservationInfo:(void *)info
+{
+ _private->observationInfo = info;
+}
+
+- (void *)observationInfo
+{
+ return _private->observationInfo;
+}
+
+- (void)_willChangeBackForwardKeys
+{
+ [self _willChangeValueForKey: _WebCanGoBackKey];
+ [self _willChangeValueForKey: _WebCanGoForwardKey];
+}
+
+- (void)_didChangeBackForwardKeys
+{
+ [self _didChangeValueForKey: _WebCanGoBackKey];
+ [self _didChangeValueForKey: _WebCanGoForwardKey];
+}
+
+- (void)_didStartProvisionalLoadForFrame:(WebFrame *)frame
+{
+ [self _willChangeBackForwardKeys];
+ if (frame == [self mainFrame]){
+ // Force an observer update by sending a will/did.
+ [self _willChangeValueForKey: _WebIsLoadingKey];
+ [self _didChangeValueForKey: _WebIsLoadingKey];
+
+ [self _willChangeValueForKey: _WebMainFrameURLKey];
+ }
+
+ [NSApp setWindowsNeedUpdate:YES];
+}
+
+- (void)_didCommitLoadForFrame:(WebFrame *)frame
+{
+ if (frame == [self mainFrame])
+ [self _didChangeValueForKey: _WebMainFrameURLKey];
+ [NSApp setWindowsNeedUpdate:YES];
+}
+
+- (void)_didFinishLoadForFrame:(WebFrame *)frame
+{
+ [self _didChangeBackForwardKeys];
+ if (frame == [self mainFrame]){
+ // Force an observer update by sending a will/did.
+ [self _willChangeValueForKey: _WebIsLoadingKey];
+ [self _didChangeValueForKey: _WebIsLoadingKey];
+ }
+ [NSApp setWindowsNeedUpdate:YES];
+}
+
+- (void)_didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
+{
+ [self _didChangeBackForwardKeys];
+ if (frame == [self mainFrame]){
+ // Force an observer update by sending a will/did.
+ [self _willChangeValueForKey: _WebIsLoadingKey];
+ [self _didChangeValueForKey: _WebIsLoadingKey];
+ }
+ [NSApp setWindowsNeedUpdate:YES];
+}
+
+- (void)_didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
+{
+ [self _didChangeBackForwardKeys];
+ if (frame == [self mainFrame]){
+ // Force an observer update by sending a will/did.
+ [self _willChangeValueForKey: _WebIsLoadingKey];
+ [self _didChangeValueForKey: _WebIsLoadingKey];
+
+ [self _didChangeValueForKey: _WebMainFrameURLKey];
+ }
+ [NSApp setWindowsNeedUpdate:YES];
+}
+
+- (void)_reloadForPluginChanges
+{
+ [[self mainFrame] _reloadForPluginChanges];
+}
+
+- (NSCachedURLResponse *)_cachedResponseForURL:(NSURL *)URL
+{
+ NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:URL];
+ [request _web_setHTTPUserAgent:[self userAgentForURL:URL]];
+ NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
+ [request release];
+ return cachedResponse;
+}
+
+- (void)_writeImageForElement:(NSDictionary *)element withPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard
+{
+ NSURL *linkURL = [element objectForKey:WebElementLinkURLKey];
+ DOMElement *domElement = [element objectForKey:WebElementDOMNodeKey];
+ [pasteboard _web_writeImage:(NSImage *)(domElement ? nil : [element objectForKey:WebElementImageKey])
+ element:domElement
+ URL:linkURL ? linkURL : (NSURL *)[element objectForKey:WebElementImageURLKey]
+ title:[element objectForKey:WebElementImageAltStringKey]
+ archive:[[element objectForKey:WebElementDOMNodeKey] webArchive]
+ types:types
+ source:nil];
+}
+
+- (void)_writeLinkElement:(NSDictionary *)element withPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard
+{
+ [pasteboard _web_writeURL:[element objectForKey:WebElementLinkURLKey]
+ andTitle:[element objectForKey:WebElementLinkLabelKey]
+ types:types];
+}
+
+- (void)_setInitiatedDrag:(BOOL)initiatedDrag
+{
+ if (!_private->page)
+ return;
+ _private->page->dragController()->setDidInitiateDrag(initiatedDrag);
+}
+
+#define DASHBOARD_CONTROL_LABEL @"control"
+
+- (void)_addScrollerDashboardRegions:(NSMutableDictionary *)regions from:(NSArray *)views
+{
+ // Add scroller regions for NSScroller and KWQScrollBar
+ int i, count = [views count];
+
+ for (i = 0; i < count; i++) {
+ NSView *aView = [views objectAtIndex:i];
+
+ if ([aView isKindOfClass:[NSScroller class]] ||
+ [aView isKindOfClass:NSClassFromString (@"KWQScrollBar")]) {
+ NSRect bounds = [aView bounds];
+ NSRect adjustedBounds;
+ adjustedBounds.origin = [self convertPoint:bounds.origin fromView:aView];
+ adjustedBounds.origin.y = [self bounds].size.height - adjustedBounds.origin.y;
+
+ // AppKit has horrible hack of placing absent scrollers at -100,-100
+ if (adjustedBounds.origin.y == -100)
+ continue;
+ adjustedBounds.size = bounds.size;
+ NSRect clip = [aView visibleRect];
+ NSRect adjustedClip;
+ adjustedClip.origin = [self convertPoint:clip.origin fromView:aView];
+ adjustedClip.origin.y = [self bounds].size.height - adjustedClip.origin.y;
+ adjustedClip.size = clip.size;
+ WebDashboardRegion *aRegion =
+ [[[WebDashboardRegion alloc] initWithRect:adjustedBounds
+ clip:adjustedClip type:WebDashboardRegionTypeScrollerRectangle] autorelease];
+ NSMutableArray *scrollerRegions;
+ scrollerRegions = [regions objectForKey:DASHBOARD_CONTROL_LABEL];
+ if (!scrollerRegions) {
+ scrollerRegions = [NSMutableArray array];
+ [regions setObject:scrollerRegions forKey:DASHBOARD_CONTROL_LABEL];
+ }
+ [scrollerRegions addObject:aRegion];
+ }
+ [self _addScrollerDashboardRegions:regions from:[aView subviews]];
+ }
+}
+
+- (void)_addScrollerDashboardRegions:(NSMutableDictionary *)regions
+{
+ [self _addScrollerDashboardRegions:regions from:[self subviews]];
+}
+
+- (NSDictionary *)_dashboardRegions
+{
+ // Only return regions from main frame.
+ Frame* mainFrame = [[[self mainFrame] _bridge] _frame];
+ if (!mainFrame)
+ return nil;
+ NSMutableDictionary *regions = mainFrame->dashboardRegionsDictionary();
+ [self _addScrollerDashboardRegions:regions];
+ return regions;
+}
+
+- (void)_setDashboardBehavior:(WebDashboardBehavior)behavior to:(BOOL)flag
+{
+ // FIXME: Remove this blanket assignment once Dashboard and Dashcode implement
+ // specific support for the backward compatibility mode flag.
+ if (behavior == WebDashboardBehaviorAllowWheelScrolling && flag == NO && _private->page)
+ _private->page->settings()->setUsesDashboardBackwardCompatibilityMode(true);
+
+ switch (behavior) {
+ case WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows: {
+ _private->dashboardBehaviorAlwaysSendMouseEventsToAllWindows = flag;
+ break;
+ }
+ case WebDashboardBehaviorAlwaysSendActiveNullEventsToPlugIns: {
+ _private->dashboardBehaviorAlwaysSendActiveNullEventsToPlugIns = flag;
+ break;
+ }
+ case WebDashboardBehaviorAlwaysAcceptsFirstMouse: {
+ _private->dashboardBehaviorAlwaysAcceptsFirstMouse = flag;
+ break;
+ }
+ case WebDashboardBehaviorAllowWheelScrolling: {
+ _private->dashboardBehaviorAllowWheelScrolling = flag;
+ break;
+ }
+ case WebDashboardBehaviorUseBackwardCompatibilityMode: {
+ if (_private->page)
+ _private->page->settings()->setUsesDashboardBackwardCompatibilityMode(flag);
+ break;
+ }
+ }
+}
+
+- (BOOL)_dashboardBehavior:(WebDashboardBehavior)behavior
+{
+ switch (behavior) {
+ case WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows: {
+ return _private->dashboardBehaviorAlwaysSendMouseEventsToAllWindows;
+ }
+ case WebDashboardBehaviorAlwaysSendActiveNullEventsToPlugIns: {
+ return _private->dashboardBehaviorAlwaysSendActiveNullEventsToPlugIns;
+ }
+ case WebDashboardBehaviorAlwaysAcceptsFirstMouse: {
+ return _private->dashboardBehaviorAlwaysAcceptsFirstMouse;
+ }
+ case WebDashboardBehaviorAllowWheelScrolling: {
+ return _private->dashboardBehaviorAllowWheelScrolling;
+ }
+ case WebDashboardBehaviorUseBackwardCompatibilityMode: {
+ return _private->page && _private->page->settings()->usesDashboardBackwardCompatibilityMode();
+ }
+ }
+ return NO;
+}
+
++ (void)_setShouldUseFontSmoothing:(BOOL)f
+{
+ WebCoreSetShouldUseFontSmoothing(f);
+}
+
++ (BOOL)_shouldUseFontSmoothing
+{
+ return WebCoreShouldUseFontSmoothing();
+}
+
++ (void)_setUsesTestModeFocusRingColor:(BOOL)f
+{
+ setUsesTestModeFocusRingColor(f);
+}
+
++ (BOOL)_usesTestModeFocusRingColor
+{
+ return usesTestModeFocusRingColor();
+}
+
+// This is only used by older versions of Safari and should be removed in a future release.
++ (NSString *)_minimumRequiredSafariBuildNumber
+{
+ return @"420+";
+}
+
+- (void)setAlwaysShowVerticalScroller:(BOOL)flag
+{
+ WebDynamicScrollBarsView *scrollview = [[[self mainFrame] frameView] _scrollView];
+ if (flag) {
+ [scrollview setVerticalScrollingMode:WebCoreScrollbarAlwaysOn andLock:YES];
+ } else {
+ [scrollview setVerticalScrollingModeLocked:NO];
+ [scrollview setVerticalScrollingMode:WebCoreScrollbarAuto];
+ }
+}
+
+- (BOOL)alwaysShowVerticalScroller
+{
+ WebDynamicScrollBarsView *scrollview = [[[self mainFrame] frameView] _scrollView];
+ return [scrollview verticalScrollingModeLocked] && [scrollview verticalScrollingMode] == WebCoreScrollbarAlwaysOn;
+}
+
+- (void)setAlwaysShowHorizontalScroller:(BOOL)flag
+{
+ WebDynamicScrollBarsView *scrollview = [[[self mainFrame] frameView] _scrollView];
+ if (flag) {
+ [scrollview setHorizontalScrollingMode:WebCoreScrollbarAlwaysOn andLock:YES];
+ } else {
+ [scrollview setHorizontalScrollingModeLocked:NO];
+ [scrollview setHorizontalScrollingMode:WebCoreScrollbarAuto];
+ }
+}
+
+- (void)setProhibitsMainFrameScrolling:(BOOL)prohibits
+{
+ Frame* mainFrame = [[[self mainFrame] _bridge] _frame];
+ if (mainFrame)
+ mainFrame->setProhibitsScrolling(prohibits);
+}
+
+- (BOOL)alwaysShowHorizontalScroller
+{
+ WebDynamicScrollBarsView *scrollview = [[[self mainFrame] frameView] _scrollView];
+ return [scrollview horizontalScrollingModeLocked] && [scrollview horizontalScrollingMode] == WebCoreScrollbarAlwaysOn;
+}
+
+- (void)_setInViewSourceMode:(BOOL)flag
+{
+ Frame* mainFrame = [[[self mainFrame] _bridge] _frame];
+ if (mainFrame)
+ mainFrame->setInViewSourceMode(flag);
+}
+
+- (BOOL)_inViewSourceMode
+{
+ Frame* mainFrame = [[[self mainFrame] _bridge] _frame];
+ return mainFrame && mainFrame->inViewSourceMode();
+}
+
+- (void)_setUseFastImageScalingMode:(BOOL)flag
+{
+ if (_private->page && _private->page->inLowQualityImageInterpolationMode() != flag) {
+ _private->page->setInLowQualityImageInterpolationMode(flag);
+ [self setNeedsDisplay:YES];
+ }
+}
+
+- (BOOL)_inFastImageScalingMode
+{
+ if (_private->page)
+ return _private->page->inLowQualityImageInterpolationMode();
+ return NO;
+}
+
+- (void)_setAdditionalWebPlugInPaths:(NSArray *)newPaths
+{
+ if (!_private->pluginDatabase)
+ _private->pluginDatabase = [[WebPluginDatabase alloc] init];
+
+ [_private->pluginDatabase setPlugInPaths:newPaths];
+ [_private->pluginDatabase refresh];
+}
+
+- (void)_attachScriptDebuggerToAllFrames
+{
+ for (Frame* frame = core([self mainFrame]); frame; frame = frame->tree()->traverseNext())
+ [kit(frame) _attachScriptDebugger];
+}
+
+- (void)_detachScriptDebuggerFromAllFrames
+{
+ for (Frame* frame = core([self mainFrame]); frame; frame = frame->tree()->traverseNext())
+ [kit(frame) _detachScriptDebugger];
+}
+
+- (void)setBackgroundColor:(NSColor *)backgroundColor
+{
+ if ([_private->backgroundColor isEqual:backgroundColor])
+ return;
+
+ id old = _private->backgroundColor;
+ _private->backgroundColor = [backgroundColor retain];
+ [old release];
+
+ [[self mainFrame] _updateBackground];
+}
+
+- (NSColor *)backgroundColor
+{
+ return _private->backgroundColor;
+}
+
+- (BOOL)defersCallbacks
+{
+ if (!_private->page)
+ return NO;
+ return _private->page->defersLoading();
+}
+
+- (void)setDefersCallbacks:(BOOL)defer
+{
+ if (!_private->page)
+ return;
+ return _private->page->setDefersLoading(defer);
+}
+
+// For backwards compatibility with the WebBackForwardList API, we honor both
+// a per-WebView and a per-preferences setting for whether to use the page cache.
+
+- (BOOL)usesPageCache
+{
+ return _private->usesPageCache && [[self preferences] usesPageCache];
+}
+
+- (void)setUsesPageCache:(BOOL)usesPageCache
+{
+ _private->usesPageCache = usesPageCache;
+
+ // Post a notification so the WebCore settings update.
+ [[self preferences] _postPreferencesChangesNotification];
+}
+
+- (void)handleAuthenticationForResource:(id)identifier challenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)dataSource
+{
+ NSWindow *window = [self hostWindow] ? [self hostWindow] : [self window];
+ [[WebPanelAuthenticationHandler sharedHandler] startAuthentication:challenge window:window];
+}
+
+- (void)_clearUndoRedoOperations
+{
+ if (!_private->page)
+ return;
+ _private->page->clearUndoRedoOperations();
+}
+
+- (void)_setCatchesDelegateExceptions:(BOOL)f
+{
+ _private->catchesDelegateExceptions = f;
+}
+
+- (BOOL)_catchesDelegateExceptions
+{
+ return _private->catchesDelegateExceptions;
+}
+
+@end
+
+@implementation _WebSafeForwarder
+
+// Used to send messages to delegates that implement informal protocols.
+
+- (id)initWithTarget:(id)t defaultTarget:(id)dt catchExceptions:(BOOL)c
+{
+ self = [super init];
+ if (!self)
+ return nil;
+ target = t; // Non retained.
+ defaultTarget = dt;
+ catchExceptions = c;
+ return self;
+}
+
+- (void)forwardInvocation:(NSInvocation *)invocation
+{
+ if ([target respondsToSelector:[invocation selector]]) {
+ if (catchExceptions) {
+ @try {
+ [invocation invokeWithTarget:target];
+ } @catch(id exception) {
+ ReportDiscardedDelegateException([invocation selector], exception);
+ }
+ } else
+ [invocation invokeWithTarget:target];
+ return;
+ }
+
+ if ([defaultTarget respondsToSelector:[invocation selector]])
+ [invocation invokeWithTarget:defaultTarget];
+
+ // Do nothing quietly if method not implemented.
+}
+
+- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
+{
+ return [defaultTarget methodSignatureForSelector:aSelector];
+}
+
+@end
+
+@implementation WebView
+
++ (void)initialize
+{
+ static BOOL initialized = NO;
+ if (initialized)
+ return;
+ initialized = YES;
+
+#ifdef REMOVE_SAFARI_DOM_TREE_DEBUG_ITEM
+ // This prevents open source users from crashing when using the Show DOM Tree menu item in Safari 2.
+ // FIXME: remove this when we no longer need to support Safari 2.
+ if ([[[NSBundle mainBundle] bundleIdentifier] isEqualToString:@"com.apple.Safari"] && [[NSUserDefaults standardUserDefaults] boolForKey:@"IncludeDebugMenu"])
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_finishedLaunching) name:NSApplicationDidFinishLaunchingNotification object:NSApp];
+#endif
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_applicationWillTerminate) name:NSApplicationWillTerminateNotification object:NSApp];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_preferencesChangedNotification:) name:WebPreferencesChangedNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_preferencesRemovedNotification:) name:WebPreferencesRemovedNotification object:nil];
+}
+
++ (void)_applicationWillTerminate
+{
+ applicationIsTerminating = YES;
+ if (!pluginDatabaseClientCount)
+ [WebPluginDatabase closeSharedDatabase];
+}
+
+#ifdef REMOVE_SAFARI_DOM_TREE_DEBUG_ITEM
+// FIXME: remove this when it is no longer needed to prevent Safari from crashing
++ (void)_finishedLaunching
+{
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_removeDOMTreeMenuItem:) name:NSMenuDidAddItemNotification object:[NSApp mainMenu]];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:NSApplicationDidFinishLaunchingNotification object:NSApp];
+}
+
++(void)_removeDOMTreeMenuItem:(NSNotification *)notification
+{
+ NSMenu *debugMenu = [[[[NSApp mainMenu] itemArray] lastObject] submenu];
+ NSMenuItem *domTree = [debugMenu itemWithTitle:@"Show DOM Tree"];
+ if (domTree)
+ [debugMenu removeItem:domTree];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:NSMenuDidAddItemNotification object:[NSApp mainMenu]];
+}
+#endif
+
++ (BOOL)canShowMIMEType:(NSString *)MIMEType
+{
+ return [self _viewClass:nil andRepresentationClass:nil forMIMEType:MIMEType];
+}
+
+- (WebBasePluginPackage *)_pluginForMIMEType:(NSString *)MIMEType
+{
+ WebBasePluginPackage *pluginPackage = [[WebPluginDatabase sharedDatabase] pluginForMIMEType:MIMEType];
+ if (pluginPackage)
+ return pluginPackage;
+
+ if (_private->pluginDatabase)
+ return [_private->pluginDatabase pluginForMIMEType:MIMEType];
+
+ return nil;
+}
+
+- (WebBasePluginPackage *)_pluginForExtension:(NSString *)extension
+{
+ WebBasePluginPackage *pluginPackage = [[WebPluginDatabase sharedDatabase] pluginForExtension:extension];
+ if (pluginPackage)
+ return pluginPackage;
+
+ if (_private->pluginDatabase)
+ return [_private->pluginDatabase pluginForExtension:extension];
+
+ return nil;
+}
+
+- (BOOL)_isMIMETypeRegisteredAsPlugin:(NSString *)MIMEType
+{
+ if ([[WebPluginDatabase sharedDatabase] isMIMETypeRegistered:MIMEType])
+ return YES;
+
+ if (_private->pluginDatabase && [_private->pluginDatabase isMIMETypeRegistered:MIMEType])
+ return YES;
+
+ return NO;
+}
+
++ (BOOL)canShowMIMETypeAsHTML:(NSString *)MIMEType
+{
+ return [WebFrameView _canShowMIMETypeAsHTML:MIMEType];
+}
+
++ (NSArray *)MIMETypesShownAsHTML
+{
+ NSMutableDictionary *viewTypes = [WebFrameView _viewTypesAllowImageTypeOmission:YES];
+ NSEnumerator *enumerator = [viewTypes keyEnumerator];
+ id key;
+ NSMutableArray *array = [[[NSMutableArray alloc] init] autorelease];
+
+ while ((key = [enumerator nextObject])) {
+ if ([viewTypes objectForKey:key] == [WebHTMLView class])
+ [array addObject:key];
+ }
+
+ return array;
+}
+
++ (void)setMIMETypesShownAsHTML:(NSArray *)MIMETypes
+{
+ NSDictionary *viewTypes = [[WebFrameView _viewTypesAllowImageTypeOmission:YES] copy];
+ NSEnumerator *enumerator = [viewTypes keyEnumerator];
+ id key;
+ while ((key = [enumerator nextObject])) {
+ if ([viewTypes objectForKey:key] == [WebHTMLView class])
+ [WebView _unregisterViewClassAndRepresentationClassForMIMEType:key];
+ }
+
+ int i, count = [MIMETypes count];
+ for (i = 0; i < count; i++) {
+ [WebView registerViewClass:[WebHTMLView class]
+ representationClass:[WebHTMLRepresentation class]
+ forMIMEType:[MIMETypes objectAtIndex:i]];
+ }
+ [viewTypes release];
+}
+
++ (NSURL *)URLFromPasteboard:(NSPasteboard *)pasteboard
+{
+ return [pasteboard _web_bestURL];
+}
+
++ (NSString *)URLTitleFromPasteboard:(NSPasteboard *)pasteboard
+{
+ return [pasteboard stringForType:WebURLNamePboardType];
+}
+
++ (void)registerURLSchemeAsLocal:(NSString *)protocol
+{
+ FrameLoader::registerURLSchemeAsLocal(protocol);
+}
+
+- (void)_registerDraggedTypes
+{
+ NSArray *editableTypes = [WebHTMLView _insertablePasteboardTypes];
+ NSArray *URLTypes = [NSPasteboard _web_dragTypesForURL];
+ NSMutableSet *types = [[NSMutableSet alloc] initWithArray:editableTypes];
+ [types addObjectsFromArray:URLTypes];
+ [self registerForDraggedTypes:[types allObjects]];
+ [types release];
+}
+
+- (void)_commonInitializationWithFrameName:(NSString *)frameName groupName:(NSString *)groupName
+{
+ WebPreferences *standardPreferences = [WebPreferences standardPreferences];
+ [standardPreferences willAddToWebView];
+
+ _private->preferences = [standardPreferences retain];
+ _private->catchesDelegateExceptions = YES;
+ _private->mainFrameDocumentReady = NO;
+ _private->drawsBackground = YES;
+ _private->smartInsertDeleteEnabled = YES;
+ _private->backgroundColor = [[NSColor whiteColor] retain];
+
+ NSRect f = [self frame];
+ WebFrameView *frameView = [[WebFrameView alloc] initWithFrame: NSMakeRect(0,0,f.size.width,f.size.height)];
+ [frameView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+ [self addSubview:frameView];
+ [frameView release];
+
+ WebKitInitializeLoggingChannelsIfNecessary();
+ WebCore::InitializeLoggingChannelsIfNecessary();
+ [WebHistoryItem initWindowWatcherIfNecessary];
+
+ _private->page = new Page(new WebChromeClient(self), new WebContextMenuClient(self), new WebEditorClient(self), new WebDragClient(self), new WebInspectorClient(self));
+ [[[WebFrameBridge alloc] initMainFrameWithPage:_private->page frameName:frameName frameView:frameView] release];
+
+ [self _addToAllWebViewsSet];
+ [self setGroupName:groupName];
+
+ // If there's already a next key view (e.g., from a nib), wire it up to our
+ // contained frame view. In any case, wire our next key view up to the our
+ // contained frame view. This works together with our becomeFirstResponder
+ // and setNextKeyView overrides.
+ NSView *nextKeyView = [self nextKeyView];
+ if (nextKeyView != nil && nextKeyView != frameView) {
+ [frameView setNextKeyView:nextKeyView];
+ }
+ [super setNextKeyView:frameView];
+
+ ++WebViewCount;
+
+ [self _registerDraggedTypes];
+
+ // initialize WebScriptDebugServer here so listeners can register before any pages are loaded.
+ if ([WebView _scriptDebuggerEnabled])
+ [WebScriptDebugServer sharedScriptDebugServer];
+
+ WebPreferences *prefs = [self preferences];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_preferencesChangedNotification:)
+ name:WebPreferencesChangedNotification object:prefs];
+
+ // Post a notification so the WebCore settings update.
+ [[self preferences] _postPreferencesChangesNotification];
+
+ if (WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_LOCAL_RESOURCE_SECURITY_RESTRICTION))
+ FrameLoader::setRestrictAccessToLocal(true);
+}
+
+- (id)initWithFrame:(NSRect)f
+{
+ return [self initWithFrame:f frameName:nil groupName:nil];
+}
+
+- (id)initWithFrame:(NSRect)f frameName:(NSString *)frameName groupName:(NSString *)groupName;
+{
+ self = [super initWithFrame:f];
+ if (!self)
+ return nil;
+
+#ifdef ENABLE_WEBKIT_UNSET_DYLD_FRAMEWORK_PATH
+ // DYLD_FRAMEWORK_PATH is used so Safari will load the development version of WebKit, which
+ // may not work with other WebKit applications. Unsetting DYLD_FRAMEWORK_PATH removes the
+ // need for Safari to unset it to prevent it from being passed to applications it launches.
+ // Unsetting it when a WebView is first created is as good a place as any.
+ // See <http://bugs.webkit.org/show_bug.cgi?id=4286> for more details.
+ if (getenv("WEBKIT_UNSET_DYLD_FRAMEWORK_PATH")) {
+ unsetenv("DYLD_FRAMEWORK_PATH");
+ unsetenv("WEBKIT_UNSET_DYLD_FRAMEWORK_PATH");
+ }
+#endif
+
+ _private = [[WebViewPrivate alloc] init];
+ [self _commonInitializationWithFrameName:frameName groupName:groupName];
+ [self setMaintainsBackForwardList: YES];
+ return self;
+}
+
+- (id)initWithCoder:(NSCoder *)decoder
+{
+ WebView *result = nil;
+
+NS_DURING
+
+ NSString *frameName;
+ NSString *groupName;
+ WebPreferences *preferences;
+ BOOL useBackForwardList = NO;
+ BOOL allowsUndo = YES;
+
+ result = [super initWithCoder:decoder];
+ result->_private = [[WebViewPrivate alloc] init];
+
+ // We don't want any of the archived subviews. The subviews will always
+ // be created in _commonInitializationFrameName:groupName:.
+ [[result subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
+
+ if ([decoder allowsKeyedCoding]) {
+ frameName = [decoder decodeObjectForKey:@"FrameName"];
+ groupName = [decoder decodeObjectForKey:@"GroupName"];
+ preferences = [decoder decodeObjectForKey:@"Preferences"];
+ useBackForwardList = [decoder decodeBoolForKey:@"UseBackForwardList"];
+ if ([decoder containsValueForKey:@"AllowsUndo"])
+ allowsUndo = [decoder decodeBoolForKey:@"AllowsUndo"];
+ } else {
+ int version;
+ [decoder decodeValueOfObjCType:@encode(int) at:&version];
+ frameName = [decoder decodeObject];
+ groupName = [decoder decodeObject];
+ preferences = [decoder decodeObject];
+ if (version > 1)
+ [decoder decodeValuesOfObjCTypes:"c", &useBackForwardList];
+ // The allowsUndo field is no longer written out in encodeWithCoder, but since there are
+ // version 3 NIBs that have this field encoded, we still need to read it in.
+ if (version == 3)
+ [decoder decodeValuesOfObjCTypes:"c", &allowsUndo];
+ }
+
+ if (![frameName isKindOfClass:[NSString class]])
+ frameName = nil;
+ if (![groupName isKindOfClass:[NSString class]])
+ groupName = nil;
+ if (![preferences isKindOfClass:[WebPreferences class]])
+ preferences = nil;
+
+ LOG(Encoding, "FrameName = %@, GroupName = %@, useBackForwardList = %d\n", frameName, groupName, (int)useBackForwardList);
+ [result _commonInitializationWithFrameName:frameName groupName:groupName];
+ [result page]->backForwardList()->setEnabled(useBackForwardList);
+ result->_private->allowsUndo = allowsUndo;
+ if (preferences)
+ [result setPreferences:preferences];
+
+NS_HANDLER
+
+ result = nil;
+ [self release];
+
+NS_ENDHANDLER
+
+ return result;
+}
+
+- (void)encodeWithCoder:(NSCoder *)encoder
+{
+ // Set asside the subviews before we archive. We don't want to archive any subviews.
+ // The subviews will always be created in _commonInitializationFrameName:groupName:.
+ id originalSubviews = _subviews;
+ _subviews = nil;
+
+ [super encodeWithCoder:encoder];
+
+ // Restore the subviews we set aside.
+ _subviews = originalSubviews;
+
+ BOOL useBackForwardList = _private->page && _private->page->backForwardList()->enabled();
+ if ([encoder allowsKeyedCoding]) {
+ [encoder encodeObject:[[self mainFrame] name] forKey:@"FrameName"];
+ [encoder encodeObject:[self groupName] forKey:@"GroupName"];
+ [encoder encodeObject:[self preferences] forKey:@"Preferences"];
+ [encoder encodeBool:useBackForwardList forKey:@"UseBackForwardList"];
+ [encoder encodeBool:_private->allowsUndo forKey:@"AllowsUndo"];
+ } else {
+ int version = WebViewVersion;
+ [encoder encodeValueOfObjCType:@encode(int) at:&version];
+ [encoder encodeObject:[[self mainFrame] name]];
+ [encoder encodeObject:[self groupName]];
+ [encoder encodeObject:[self preferences]];
+ [encoder encodeValuesOfObjCTypes:"c", &useBackForwardList];
+ // DO NOT encode any new fields here, doing so will break older WebKit releases.
+ }
+
+ LOG(Encoding, "FrameName = %@, GroupName = %@, useBackForwardList = %d\n", [[self mainFrame] name], [self groupName], (int)useBackForwardList);
+}
+
+- (void)dealloc
+{
+ // call close to ensure we tear-down completely
+ // this maintains our old behavior for existing applications
+ [self _close];
+
+ --WebViewCount;
+
+ [_private release];
+ // [super dealloc] can end up dispatching against _private (3466082)
+ _private = nil;
+
+ [super dealloc];
+}
+
+- (void)finalize
+{
+ ASSERT(_private->closed);
+
+ --WebViewCount;
+
+ [super finalize];
+}
+
+- (void)close
+{
+ [self _close];
+}
+
+- (void)setShouldCloseWithWindow:(BOOL)close
+{
+ _private->shouldCloseWithWindow = close;
+}
+
+- (BOOL)shouldCloseWithWindow
+{
+ return _private->shouldCloseWithWindow;
+}
+
+- (void)viewWillMoveToWindow:(NSWindow *)window
+{
+ // Don't do anything if we aren't initialized. This happens when decoding a WebView.
+ if (!_private)
+ return;
+
+ if ([self window])
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowWillCloseNotification object:[self window]];
+
+ if (window) {
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowWillClose:) name:NSWindowWillCloseNotification object:window];
+
+ // Ensure that we will receive the events that WebHTMLView (at least) needs. It's expensive enough
+ // that we don't want to call it over and over.
+ [window setAcceptsMouseMovedEvents:YES];
+ WKSetNSWindowShouldPostEventNotifications(window, YES);
+ }
+}
+
+- (void)_windowWillClose:(NSNotification *)notification
+{
+ if ([self shouldCloseWithWindow] && ([self window] == [self hostWindow] || ([self window] && ![self hostWindow]) || (![self window] && [self hostWindow])))
+ [self _close];
+}
+
+- (void)setPreferences:(WebPreferences *)prefs
+{
+ if (!prefs)
+ prefs = [WebPreferences standardPreferences];
+
+ if (_private->preferences == prefs)
+ return;
+
+ [prefs willAddToWebView];
+
+ WebPreferences *oldPrefs = _private->preferences;
+
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:[self preferences]];
+ [WebPreferences _removeReferenceForIdentifier:[oldPrefs identifier]];
+
+ _private->preferences = [prefs retain];
+
+ // After registering for the notification, post it so the WebCore settings update.
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_preferencesChangedNotification:)
+ name:WebPreferencesChangedNotification object:[self preferences]];
+ [[self preferences] _postPreferencesChangesNotification];
+
+ [oldPrefs didRemoveFromWebView];
+ [oldPrefs release];
+}
+
+- (WebPreferences *)preferences
+{
+ return _private->preferences;
+}
+
+- (void)setPreferencesIdentifier:(NSString *)anIdentifier
+{
+ if (!_private->closed && ![anIdentifier isEqual:[[self preferences] identifier]]) {
+ WebPreferences *prefs = [[WebPreferences alloc] initWithIdentifier:anIdentifier];
+ [self setPreferences:prefs];
+ [prefs release];
+ }
+}
+
+- (NSString *)preferencesIdentifier
+{
+ return [[self preferences] identifier];
+}
+
+
+- (void)setUIDelegate:delegate
+{
+ _private->UIDelegate = delegate;
+ [_private->UIDelegateForwarder release];
+ _private->UIDelegateForwarder = nil;
+}
+
+- UIDelegate
+{
+ return _private->UIDelegate;
+}
+
+- (void)setResourceLoadDelegate: delegate
+{
+ _private->resourceProgressDelegate = delegate;
+ [self _cacheResourceLoadDelegateImplementations];
+}
+
+- resourceLoadDelegate
+{
+ return _private->resourceProgressDelegate;
+}
+
+- (void)setDownloadDelegate: delegate
+{
+ _private->downloadDelegate = delegate;
+}
+
+
+- downloadDelegate
+{
+ return _private->downloadDelegate;
+}
+
+- (void)setPolicyDelegate:delegate
+{
+ _private->policyDelegate = delegate;
+ [_private->policyDelegateForwarder release];
+ _private->policyDelegateForwarder = nil;
+}
+
+- policyDelegate
+{
+ return _private->policyDelegate;
+}
+
+- (void)setFrameLoadDelegate:delegate
+{
+ _private->frameLoadDelegate = delegate;
+ [self _cacheFrameLoadDelegateImplementations];
+}
+
+- frameLoadDelegate
+{
+ return _private->frameLoadDelegate;
+}
+
+- (WebFrame *)mainFrame
+{
+ // This can be called in initialization, before _private has been set up (3465613)
+ if (!_private)
+ return nil;
+ if (!_private->page)
+ return nil;
+ return kit(_private->page->mainFrame());
+}
+
+- (WebFrame *)selectedFrame
+{
+ // If the first responder is a view in our tree, we get the frame containing the first responder.
+ // This is faster than searching the frame hierarchy, and will give us a result even in the case
+ // where the focused frame doesn't actually contain a selection.
+ WebFrame *focusedFrame = [self _focusedFrame];
+ if (focusedFrame)
+ return focusedFrame;
+
+ // If the first responder is outside of our view tree, we search for a frame containing a selection.
+ // There should be at most only one of these.
+ return [[self mainFrame] _findFrameWithSelection];
+}
+
+- (WebBackForwardList *)backForwardList
+{
+ if (!_private->page)
+ return nil;
+ if (!_private->page->backForwardList()->enabled())
+ return nil;
+ return kit(_private->page->backForwardList());
+}
+
+- (void)setMaintainsBackForwardList: (BOOL)flag
+{
+ if (!_private->page)
+ return;
+ _private->page->backForwardList()->setEnabled(flag);
+}
+
+- (BOOL)goBack
+{
+ if (!_private->page)
+ return NO;
+
+ return _private->page->goBack();
+}
+
+- (BOOL)goForward
+{
+ if (!_private->page)
+ return NO;
+
+ return _private->page->goForward();
+}
+
+- (BOOL)goToBackForwardItem:(WebHistoryItem *)item
+{
+ if (!_private->page)
+ return NO;
+
+ _private->page->goToItem(core(item), FrameLoadTypeIndexedBackForward);
+ return YES;
+}
+
+- (void)setTextSizeMultiplier:(float)m
+{
+ // NOTE: This has no visible effect when viewing a PDF (see <rdar://problem/4737380>)
+ if (_private->textSizeMultiplier == m)
+ return;
+
+ _private->textSizeMultiplier = m;
+ [self _notifyTextSizeMultiplierChanged];
+}
+
+- (float)textSizeMultiplier
+{
+ return _private->textSizeMultiplier;
+}
+
+- (void)setApplicationNameForUserAgent:(NSString *)applicationName
+{
+ NSString *name = [applicationName copy];
+ [_private->applicationNameForUserAgent release];
+ _private->applicationNameForUserAgent = name;
+ if (!_private->userAgentOverridden)
+ *_private->userAgent = String();
+}
+
+- (NSString *)applicationNameForUserAgent
+{
+ return [[_private->applicationNameForUserAgent retain] autorelease];
+}
+
+- (void)setCustomUserAgent:(NSString *)userAgentString
+{
+ *_private->userAgent = userAgentString;
+ _private->userAgentOverridden = userAgentString != nil;
+}
+
+- (NSString *)customUserAgent
+{
+ if (!_private->userAgentOverridden)
+ return nil;
+ return *_private->userAgent;
+}
+
+- (void)setMediaStyle:(NSString *)mediaStyle
+{
+ if (_private->mediaStyle != mediaStyle) {
+ [_private->mediaStyle release];
+ _private->mediaStyle = [mediaStyle copy];
+ }
+}
+
+- (NSString *)mediaStyle
+{
+ return _private->mediaStyle;
+}
+
+- (BOOL)supportsTextEncoding
+{
+ id documentView = [[[self mainFrame] frameView] documentView];
+ return [documentView conformsToProtocol:@protocol(WebDocumentText)]
+ && [documentView supportsTextEncoding];
+}
+
+- (void)setCustomTextEncodingName:(NSString *)encoding
+{
+ NSString *oldEncoding = [self customTextEncodingName];
+ if (encoding == oldEncoding || [encoding isEqualToString:oldEncoding])
+ return;
+ FrameLoader* mainFrameLoader = [[self mainFrame] _frameLoader];
+ if (mainFrameLoader)
+ mainFrameLoader->reloadAllowingStaleData(encoding);
+}
+
+- (NSString *)_mainFrameOverrideEncoding
+{
+ WebDataSource *dataSource = [[self mainFrame] provisionalDataSource];
+ if (dataSource == nil)
+ dataSource = [[self mainFrame] _dataSource];
+ if (dataSource == nil)
+ return nil;
+ return nsStringNilIfEmpty([dataSource _documentLoader]->overrideEncoding());
+}
+
+- (NSString *)customTextEncodingName
+{
+ return [self _mainFrameOverrideEncoding];
+}
+
+- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script
+{
+ // FIXME: We can remove this workaround for VitalSource Bookshelf when they update
+ // their code so that it no longer calls stringByEvaluatingJavaScriptFromString with a return statement.
+ // Return statements are only valid in a function. See <rdar://problem/5095515> for the evangelism bug.
+ if (!WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_VITALSOURCE_QUIRK) && [[[NSBundle mainBundle] bundleIdentifier] isEqualToString:@"com.vitalsource.bookshelf"]) {
+ NSRange returnStringRange = [script rangeOfString:@"return "];
+ if (returnStringRange.length != 0 && returnStringRange.location == 0)
+ script = [script substringFromIndex: returnStringRange.location + returnStringRange.length];
+ }
+
+ NSString *result = [[[self mainFrame] _bridge] stringByEvaluatingJavaScriptFromString:script];
+ // The only way stringByEvaluatingJavaScriptFromString can return nil is if the frame was removed by the script
+ // Since there's no way to get rid of the main frame, result will never ever be nil here.
+ ASSERT(result);
+
+ return result;
+}
+
+- (WebScriptObject *)windowScriptObject
+{
+ Frame* coreFrame = core([self mainFrame]);
+ if (!coreFrame)
+ return nil;
+ return coreFrame->windowScriptObject();
+}
+
+// Get the appropriate user-agent string for a particular URL.
+- (NSString *)userAgentForURL:(NSURL *)url
+{
+ return [self _userAgentForURL:KURL([url absoluteURL])];
+}
+
+- (void)setHostWindow:(NSWindow *)hostWindow
+{
+ if (!_private->closed && hostWindow != _private->hostWindow) {
+ [[self mainFrame] _viewWillMoveToHostWindow:hostWindow];
+ if (_private->hostWindow)
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowWillCloseNotification object:_private->hostWindow];
+ if (hostWindow)
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowWillClose:) name:NSWindowWillCloseNotification object:hostWindow];
+ [_private->hostWindow release];
+ _private->hostWindow = [hostWindow retain];
+ [[self mainFrame] _viewDidMoveToHostWindow];
+ }
+}
+
+- (NSWindow *)hostWindow
+{
+ return _private->hostWindow;
+}
+
+- (NSView <WebDocumentView> *)documentViewAtWindowPoint:(NSPoint)point
+{
+ return [[self _frameViewAtWindowPoint:point] documentView];
+}
+
+- (NSDictionary *)_elementAtWindowPoint:(NSPoint)windowPoint
+{
+ WebFrameView *frameView = [self _frameViewAtWindowPoint:windowPoint];
+ if (!frameView)
+ return nil;
+ NSView <WebDocumentView> *documentView = [frameView documentView];
+ if ([documentView conformsToProtocol:@protocol(WebDocumentElement)]) {
+ NSPoint point = [documentView convertPoint:windowPoint fromView:nil];
+ return [(NSView <WebDocumentElement> *)documentView elementAtPoint:point];
+ }
+ return [NSDictionary dictionaryWithObject:[frameView webFrame] forKey:WebElementFrameKey];
+}
+
+- (NSDictionary *)elementAtPoint:(NSPoint)point
+{
+ return [self _elementAtWindowPoint:[self convertPoint:point toView:nil]];
+}
+
+// The following 2 internal NSView methods are called on the drag destination by make scrolling while dragging work.
+// Scrolling while dragging will only work if the drag destination is in a scroll view. The WebView is the drag destination.
+// When dragging to a WebView, the document subview should scroll, but it doesn't because it is not the drag destination.
+// Forward these calls to the document subview to make its scroll view scroll.
+- (void)_autoscrollForDraggingInfo:(id)draggingInfo timeDelta:(NSTimeInterval)repeatDelta
+{
+ NSView <WebDocumentView> *documentView = [self documentViewAtWindowPoint:[draggingInfo draggingLocation]];
+ [documentView _autoscrollForDraggingInfo:draggingInfo timeDelta:repeatDelta];
+}
+
+- (BOOL)_shouldAutoscrollForDraggingInfo:(id)draggingInfo
+{
+ NSView <WebDocumentView> *documentView = [self documentViewAtWindowPoint:[draggingInfo draggingLocation]];
+ return [documentView _shouldAutoscrollForDraggingInfo:draggingInfo];
+}
+
+- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)draggingInfo
+{
+ NSView <WebDocumentView>* view = [self documentViewAtWindowPoint:[draggingInfo draggingLocation]];
+ WebPasteboardHelper helper([view isKindOfClass:[WebHTMLView class]] ? (WebHTMLView*)view : nil);
+ IntPoint client([draggingInfo draggingLocation]);
+ IntPoint global(globalPoint([draggingInfo draggingLocation], [self window]));
+ DragData dragData(draggingInfo, client, global, (DragOperation)[draggingInfo draggingSourceOperationMask], &helper);
+ return core(self)->dragController()->dragEntered(&dragData);
+}
+
+- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)draggingInfo
+{
+ NSView <WebDocumentView>* view = [self documentViewAtWindowPoint:[draggingInfo draggingLocation]];
+ WebPasteboardHelper helper([view isKindOfClass:[WebHTMLView class]] ? (WebHTMLView*)view : nil);
+ IntPoint client([draggingInfo draggingLocation]);
+ IntPoint global(globalPoint([draggingInfo draggingLocation], [self window]));
+ DragData dragData(draggingInfo, client, global, (DragOperation)[draggingInfo draggingSourceOperationMask], &helper);
+ return core(self)->dragController()->dragUpdated(&dragData);
+}
+
+- (void)draggingExited:(id <NSDraggingInfo>)draggingInfo
+{
+ NSView <WebDocumentView>* view = [self documentViewAtWindowPoint:[draggingInfo draggingLocation]];
+ WebPasteboardHelper helper([view isKindOfClass:[WebHTMLView class]] ? (WebHTMLView*)view : nil);
+ IntPoint client([draggingInfo draggingLocation]);
+ IntPoint global(globalPoint([draggingInfo draggingLocation], [self window]));
+ DragData dragData(draggingInfo, client, global, (DragOperation)[draggingInfo draggingSourceOperationMask], &helper);
+ core(self)->dragController()->dragExited(&dragData);
+}
+
+- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)draggingInfo
+{
+ return YES;
+}
+
+- (BOOL)performDragOperation:(id <NSDraggingInfo>)draggingInfo
+{
+ NSView <WebDocumentView>* view = [self documentViewAtWindowPoint:[draggingInfo draggingLocation]];
+ WebPasteboardHelper helper([view isKindOfClass:[WebHTMLView class]]? (WebHTMLView*)view : nil);
+ IntPoint client([draggingInfo draggingLocation]);
+ IntPoint global(globalPoint([draggingInfo draggingLocation], [self window]));
+ DragData dragData(draggingInfo, client, global, (DragOperation)[draggingInfo draggingSourceOperationMask], &helper);
+ return core(self)->dragController()->performDrag(&dragData);
+}
+
+- (NSView *)_hitTest:(NSPoint *)aPoint dragTypes:(NSSet *)types
+{
+ NSView *hitView = [super _hitTest:aPoint dragTypes:types];
+ if (!hitView && [[self superview] mouse:*aPoint inRect:[self frame]]) {
+ return self;
+ } else {
+ return hitView;
+ }
+}
+
+- (BOOL)acceptsFirstResponder
+{
+ return [[[self mainFrame] frameView] acceptsFirstResponder];
+}
+
+- (BOOL)becomeFirstResponder
+{
+ if (_private->becomingFirstResponder) {
+ // Fix for unrepro infinite recursion reported in radar 4448181. If we hit this assert on
+ // a debug build, we should figure out what causes the problem and do a better fix.
+ ASSERT_NOT_REACHED();
+ return NO;
+ }
+
+ // This works together with setNextKeyView to splice the WebView into
+ // the key loop similar to the way NSScrollView does this. Note that
+ // WebFrameView has very similar code.
+ NSWindow *window = [self window];
+ WebFrameView *mainFrameView = [[self mainFrame] frameView];
+
+ NSResponder *previousFirstResponder = [[self window] _oldFirstResponderBeforeBecoming];
+ BOOL fromOutside = ![previousFirstResponder isKindOfClass:[NSView class]] || (![(NSView *)previousFirstResponder isDescendantOf:self] && previousFirstResponder != self);
+
+ if ([window keyViewSelectionDirection] == NSSelectingPrevious) {
+ NSView *previousValidKeyView = [self previousValidKeyView];
+ if ((previousValidKeyView != self) && (previousValidKeyView != mainFrameView)) {
+ _private->becomingFirstResponder = YES;
+ _private->becomingFirstResponderFromOutside = fromOutside;
+ [window makeFirstResponder:previousValidKeyView];
+ _private->becomingFirstResponderFromOutside = NO;
+ _private->becomingFirstResponder = NO;
+ return YES;
+ } else {
+ return NO;
+ }
+ }
+
+ if ([mainFrameView acceptsFirstResponder]) {
+ _private->becomingFirstResponder = YES;
+ _private->becomingFirstResponderFromOutside = fromOutside;
+ [window makeFirstResponder:mainFrameView];
+ _private->becomingFirstResponderFromOutside = NO;
+ _private->becomingFirstResponder = NO;
+ return YES;
+ }
+
+ return NO;
+}
+
+- (NSView *)_webcore_effectiveFirstResponder
+{
+ WebFrameView *frameView = [[self mainFrame] frameView];
+ return frameView ? [frameView _webcore_effectiveFirstResponder] : [super _webcore_effectiveFirstResponder];
+}
+
+- (void)setNextKeyView:(NSView *)aView
+{
+ // This works together with becomeFirstResponder to splice the WebView into
+ // the key loop similar to the way NSScrollView does this. Note that
+ // WebFrameView has very similar code.
+ WebFrameView *mainFrameView = [[self mainFrame] frameView];
+ if (mainFrameView != nil) {
+ [mainFrameView setNextKeyView:aView];
+ } else {
+ [super setNextKeyView:aView];
+ }
+}
+
+static WebFrame *incrementFrame(WebFrame *curr, BOOL forward, BOOL wrapFlag)
+{
+ Frame* coreFrame = core(curr);
+ return kit(forward
+ ? coreFrame->tree()->traverseNextWithWrap(wrapFlag)
+ : coreFrame->tree()->traversePreviousWithWrap(wrapFlag));
+}
+
+- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag
+{
+ return [self searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag startInSelection:NO];
+}
+
++ (void)registerViewClass:(Class)viewClass representationClass:(Class)representationClass forMIMEType:(NSString *)MIMEType
+{
+ [[WebFrameView _viewTypesAllowImageTypeOmission:YES] setObject:viewClass forKey:MIMEType];
+ [[WebDataSource _repTypesAllowImageTypeOmission:YES] setObject:representationClass forKey:MIMEType];
+
+ // FIXME: We also need to maintain MIMEType registrations (which can be dynamically changed)
+ // in the WebCore MIMEType registry. For now we're doing this in a safe, limited manner
+ // to fix <rdar://problem/5372989> - a future revamping of the entire system is neccesary for future robustness
+ if ([viewClass class] == [WebHTMLView class])
+ MIMETypeRegistry::getSupportedNonImageMIMETypes().add(MIMEType);
+}
+
+- (void)setGroupName:(NSString *)groupName
+{
+ if (!_private->page)
+ return;
+ _private->page->setGroupName(groupName);
+}
+
+- (NSString *)groupName
+{
+ if (!_private->page)
+ return nil;
+ return _private->page->groupName();
+}
+
+- (double)estimatedProgress
+{
+ if (!_private->page)
+ return 0.0;
+
+ return _private->page->progress()->estimatedProgress();
+}
+
+- (NSArray *)pasteboardTypesForSelection
+{
+ NSView <WebDocumentView> *documentView = [[[self _selectedOrMainFrame] frameView] documentView];
+ if ([documentView conformsToProtocol:@protocol(WebDocumentSelection)]) {
+ return [(NSView <WebDocumentSelection> *)documentView pasteboardTypesForSelection];
+ }
+ return [NSArray array];
+}
+
+- (void)writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard
+{
+ WebFrame *frame = [self _selectedOrMainFrame];
+ if (frame && [frame _hasSelection]) {
+ NSView <WebDocumentView> *documentView = [[frame frameView] documentView];
+ if ([documentView conformsToProtocol:@protocol(WebDocumentSelection)])
+ [(NSView <WebDocumentSelection> *)documentView writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
+ }
+}
+
+- (NSArray *)pasteboardTypesForElement:(NSDictionary *)element
+{
+ if ([element objectForKey:WebElementImageURLKey] != nil) {
+ return [NSPasteboard _web_writableTypesForImageIncludingArchive:([element objectForKey:WebElementDOMNodeKey] != nil)];
+ } else if ([element objectForKey:WebElementLinkURLKey] != nil) {
+ return [NSPasteboard _web_writableTypesForURL];
+ } else if ([[element objectForKey:WebElementIsSelectedKey] boolValue]) {
+ return [self pasteboardTypesForSelection];
+ }
+ return [NSArray array];
+}
+
+- (void)writeElement:(NSDictionary *)element withPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard
+{
+ if ([element objectForKey:WebElementImageURLKey] != nil) {
+ [self _writeImageForElement:element withPasteboardTypes:types toPasteboard:pasteboard];
+ } else if ([element objectForKey:WebElementLinkURLKey] != nil) {
+ [self _writeLinkElement:element withPasteboardTypes:types toPasteboard:pasteboard];
+ } else if ([[element objectForKey:WebElementIsSelectedKey] boolValue]) {
+ [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard];
+ }
+}
+
+- (void)moveDragCaretToPoint:(NSPoint)point
+{
+ if (Page* page = core(self))
+ page->dragController()->placeDragCaret(IntPoint([self convertPoint:point toView:nil]));
+}
+
+- (void)removeDragCaret
+{
+ if (Page* page = core(self))
+ page->dragController()->dragEnded();
+}
+
+- (void)setMainFrameURL:(NSString *)URLString
+{
+ [[self mainFrame] loadRequest: [NSURLRequest requestWithURL: [NSURL _web_URLWithDataAsString: URLString]]];
+}
+
+- (NSString *)mainFrameURL
+{
+ WebDataSource *ds;
+ ds = [[self mainFrame] provisionalDataSource];
+ if (!ds)
+ ds = [[self mainFrame] _dataSource];
+ return [[[ds request] URL] _web_originalDataAsString];
+}
+
+- (BOOL)isLoading
+{
+ LOG (Bindings, "isLoading = %d", (int)[self _isLoading]);
+ return [self _isLoading];
+}
+
+- (NSString *)mainFrameTitle
+{
+ NSString *mainFrameTitle = [[[self mainFrame] _dataSource] pageTitle];
+ return (mainFrameTitle != nil) ? mainFrameTitle : (NSString *)@"";
+}
+
+- (NSImage *)mainFrameIcon
+{
+ return [[WebIconDatabase sharedIconDatabase] iconForURL:[[[[self mainFrame] _dataSource] _URL] _web_originalDataAsString] withSize:WebIconSmallSize];
+}
+
+- (DOMDocument *)mainFrameDocument
+{
+ // only return the actual value if the state we're in gives NSTreeController
+ // enough time to release its observers on the old model
+ if (_private->mainFrameDocumentReady)
+ return [[self mainFrame] DOMDocument];
+ return nil;
+}
+
+- (void)setDrawsBackground:(BOOL)drawsBackground
+{
+ if (_private->drawsBackground == drawsBackground)
+ return;
+ _private->drawsBackground = drawsBackground;
+ [[self mainFrame] _updateBackground];
+}
+
+- (BOOL)drawsBackground
+{
+ return _private->drawsBackground;
+}
+
+@end
+
+@implementation WebView (WebIBActions)
+
+- (IBAction)takeStringURLFrom: sender
+{
+ NSString *URLString = [sender stringValue];
+
+ [[self mainFrame] loadRequest: [NSURLRequest requestWithURL: [NSURL _web_URLWithDataAsString: URLString]]];
+}
+
+- (BOOL)canGoBack
+{
+ if (!_private->page)
+ return NO;
+
+ return !!_private->page->backForwardList()->backItem();
+}
+
+- (BOOL)canGoForward
+{
+ if (!_private->page)
+ return NO;
+
+ return !!_private->page->backForwardList()->forwardItem();
+}
+
+- (IBAction)goBack:(id)sender
+{
+ [self goBack];
+}
+
+- (IBAction)goForward:(id)sender
+{
+ [self goForward];
+}
+
+- (IBAction)stopLoading:(id)sender
+{
+ [[self mainFrame] stopLoading];
+}
+
+- (IBAction)reload:(id)sender
+{
+ [[self mainFrame] reload];
+}
+
+#define MinimumTextSizeMultiplier 0.5f
+#define MaximumTextSizeMultiplier 3.0f
+#define TextSizeMultiplierRatio 1.2f
+
+- (BOOL)canMakeTextSmaller
+{
+ BOOL canShrinkMore = _private->textSizeMultiplier/TextSizeMultiplierRatio > MinimumTextSizeMultiplier;
+ return [self _performTextSizingSelector:(SEL)0 withObject:nil onTrackingDocs:canShrinkMore selForNonTrackingDocs:@selector(_canMakeTextSmaller) newScaleFactor:0];
+}
+
+- (BOOL)canMakeTextLarger
+{
+ BOOL canGrowMore = _private->textSizeMultiplier*TextSizeMultiplierRatio < MaximumTextSizeMultiplier;
+ return [self _performTextSizingSelector:(SEL)0 withObject:nil onTrackingDocs:canGrowMore selForNonTrackingDocs:@selector(_canMakeTextLarger) newScaleFactor:0];
+}
+
+- (IBAction)makeTextSmaller:(id)sender
+{
+ float newScale = _private->textSizeMultiplier / TextSizeMultiplierRatio;
+ BOOL canShrinkMore = newScale > MinimumTextSizeMultiplier;
+ [self _performTextSizingSelector:@selector(_makeTextSmaller:) withObject:sender onTrackingDocs:canShrinkMore selForNonTrackingDocs:@selector(_canMakeTextSmaller) newScaleFactor:newScale];
+}
+
+- (IBAction)makeTextLarger:(id)sender
+{
+ float newScale = _private->textSizeMultiplier*TextSizeMultiplierRatio;
+ BOOL canGrowMore = newScale < MaximumTextSizeMultiplier;
+ [self _performTextSizingSelector:@selector(_makeTextLarger:) withObject:sender onTrackingDocs:canGrowMore selForNonTrackingDocs:@selector(_canMakeTextLarger) newScaleFactor:newScale];
+}
+
+- (IBAction)toggleSmartInsertDelete:(id)sender
+{
+ [self setSmartInsertDeleteEnabled:![self smartInsertDeleteEnabled]];
+}
+
+- (IBAction)toggleContinuousSpellChecking:(id)sender
+{
+ [self setContinuousSpellCheckingEnabled:![self isContinuousSpellCheckingEnabled]];
+}
+
+- (BOOL)_responderValidateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
+{
+ id responder = [self _responderForResponderOperations];
+ if (responder != self && [responder respondsToSelector:[item action]]) {
+ if ([responder respondsToSelector:@selector(validateUserInterfaceItemWithoutDelegate:)])
+ return [responder validateUserInterfaceItemWithoutDelegate:item];
+ if ([responder respondsToSelector:@selector(validateUserInterfaceItem:)])
+ return [responder validateUserInterfaceItem:item];
+ return YES;
+ }
+ return NO;
+}
+
+- (BOOL)canMakeTextStandardSize
+{
+ BOOL notAlreadyStandard = _private->textSizeMultiplier != 1.0f;
+ return [self _performTextSizingSelector:(SEL)0 withObject:nil onTrackingDocs:notAlreadyStandard selForNonTrackingDocs:@selector(_canMakeTextStandardSize) newScaleFactor:0.0f];
+}
+
+- (IBAction)makeTextStandardSize:(id)sender
+{
+ BOOL notAlreadyStandard = _private->textSizeMultiplier != 1.0f;
+ [self _performTextSizingSelector:@selector(_makeTextStandardSize:) withObject:sender onTrackingDocs:notAlreadyStandard selForNonTrackingDocs:@selector(_canMakeTextStandardSize) newScaleFactor:1.0f];
+}
+
+#define VALIDATE(name) \
+ else if (action == @selector(name:)) { return [self _responderValidateUserInterfaceItem:item]; }
+
+- (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item
+{
+ SEL action = [item action];
+
+ if (action == @selector(goBack:)) {
+ return [self canGoBack];
+ } else if (action == @selector(goForward:)) {
+ return [self canGoForward];
+ } else if (action == @selector(makeTextLarger:)) {
+ return [self canMakeTextLarger];
+ } else if (action == @selector(makeTextSmaller:)) {
+ return [self canMakeTextSmaller];
+ } else if (action == @selector(makeTextStandardSize:)) {
+ return [self canMakeTextStandardSize];
+ } else if (action == @selector(reload:)) {
+ return [[self mainFrame] _dataSource] != nil;
+ } else if (action == @selector(stopLoading:)) {
+ return [self _isLoading];
+ } else if (action == @selector(toggleContinuousSpellChecking:)) {
+ BOOL checkMark = NO;
+ BOOL retVal = NO;
+ if ([self _continuousCheckingAllowed]) {
+ checkMark = [self isContinuousSpellCheckingEnabled];
+ retVal = YES;
+ }
+ if ([(NSObject *)item isKindOfClass:[NSMenuItem class]]) {
+ NSMenuItem *menuItem = (NSMenuItem *)item;
+ [menuItem setState:checkMark ? NSOnState : NSOffState];
+ }
+ return retVal;
+#ifndef BUILDING_ON_TIGER
+ } else if (action == @selector(toggleGrammarChecking:)) {
+ BOOL checkMark = [self isGrammarCheckingEnabled];
+ if ([(NSObject *)item isKindOfClass:[NSMenuItem class]]) {
+ NSMenuItem *menuItem = (NSMenuItem *)item;
+ [menuItem setState:checkMark ? NSOnState : NSOffState];
+ }
+ return YES;
+#endif
+ }
+ FOR_EACH_RESPONDER_SELECTOR(VALIDATE)
+
+ return YES;
+}
+
+- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item
+{
+ BOOL result = [self validateUserInterfaceItemWithoutDelegate:item];
+ return CallUIDelegateReturningBoolean(result, self, @selector(webView:validateUserInterfaceItem:defaultValidation:), item, result);
+}
+
+@end
+
+@implementation WebView (WebPendingPublic)
+
+- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection
+{
+ if (_private->closed)
+ return NO;
+
+ // Get the frame holding the selection, or start with the main frame
+ WebFrame *startFrame = [self _selectedOrMainFrame];
+
+ // Search the first frame, then all the other frames, in order
+ NSView <WebDocumentSearching> *startSearchView = nil;
+ WebFrame *frame = startFrame;
+ do {
+ WebFrame *nextFrame = incrementFrame(frame, forward, wrapFlag);
+
+ BOOL onlyOneFrame = (frame == nextFrame);
+ ASSERT(!onlyOneFrame || frame == startFrame);
+
+ id <WebDocumentView> view = [[frame frameView] documentView];
+ if ([view conformsToProtocol:@protocol(WebDocumentSearching)]) {
+ NSView <WebDocumentSearching> *searchView = (NSView <WebDocumentSearching> *)view;
+
+ if (frame == startFrame)
+ startSearchView = searchView;
+
+ BOOL foundString;
+ // In some cases we have to search some content twice; see comment later in this method.
+ // We can avoid ever doing this in the common one-frame case by passing YES for wrapFlag
+ // here, and then bailing out before we get to the code that would search again in the
+ // same content.
+ BOOL wrapOnThisPass = wrapFlag && onlyOneFrame;
+ if ([searchView conformsToProtocol:@protocol(WebDocumentIncrementalSearching)])
+ foundString = [(NSView <WebDocumentIncrementalSearching> *)searchView searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapOnThisPass startInSelection:startInSelection];
+ else
+ foundString = [searchView searchFor:string direction:forward caseSensitive:caseFlag wrap:wrapOnThisPass];
+
+ if (foundString) {
+ if (frame != startFrame)
+ [startFrame _clearSelection];
+ [[self window] makeFirstResponder:searchView];
+ return YES;
+ }
+
+ if (onlyOneFrame)
+ return NO;
+ }
+ frame = nextFrame;
+ } while (frame && frame != startFrame);
+
+ // If there are multiple frames and wrapFlag is true and we've visited each one without finding a result, we still need to search in the
+ // first-searched frame up to the selection. However, the API doesn't provide a way to search only up to a particular point. The only
+ // way to make sure the entire frame is searched is to pass YES for the wrapFlag. When there are no matches, this will search again
+ // some content that we already searched on the first pass. In the worst case, we could search the entire contents of this frame twice.
+ // To fix this, we'd need to add a mechanism to specify a range in which to search.
+ if (wrapFlag && startSearchView) {
+ BOOL foundString;
+ if ([startSearchView conformsToProtocol:@protocol(WebDocumentIncrementalSearching)])
+ foundString = [(NSView <WebDocumentIncrementalSearching> *)startSearchView searchFor:string direction:forward caseSensitive:caseFlag wrap:YES startInSelection:startInSelection];
+ else
+ foundString = [startSearchView searchFor:string direction:forward caseSensitive:caseFlag wrap:YES];
+ if (foundString) {
+ [[self window] makeFirstResponder:startSearchView];
+ return YES;
+ }
+ }
+ return NO;
+}
+
+- (void)setHoverFeedbackSuspended:(BOOL)newValue
+{
+ if (_private->hoverFeedbackSuspended == newValue)
+ return;
+
+ _private->hoverFeedbackSuspended = newValue;
+ id <WebDocumentView> documentView = [[[self mainFrame] frameView] documentView];
+ // FIXME: in a perfect world we'd do this in a general way that worked with any document view,
+ // such as by calling a protocol method or using respondsToSelector or sending a notification.
+ // But until there is any need for these more general solutions, we'll just hardwire it to work
+ // with WebHTMLView.
+ // Note that _hoverFeedbackSuspendedChanged needs to be called only on the main WebHTMLView, not
+ // on each subframe separately.
+ if ([documentView isKindOfClass:[WebHTMLView class]])
+ [(WebHTMLView *)documentView _hoverFeedbackSuspendedChanged];
+}
+
+- (BOOL)isHoverFeedbackSuspended
+{
+ return _private->hoverFeedbackSuspended;
+}
+
+- (void)setMainFrameDocumentReady:(BOOL)mainFrameDocumentReady
+{
+ // by setting this to NO, calls to mainFrameDocument are forced to return nil
+ // setting this to YES lets it return the actual DOMDocument value
+ // we use this to tell NSTreeController to reset its observers and clear its state
+ if (_private->mainFrameDocumentReady == mainFrameDocumentReady)
+ return;
+ [self _willChangeValueForKey:_WebMainFrameDocumentKey];
+ _private->mainFrameDocumentReady = mainFrameDocumentReady;
+ [self _didChangeValueForKey:_WebMainFrameDocumentKey];
+ // this will cause observers to call mainFrameDocument where this flag will be checked
+}
+
+// This method name is used by Mail on Tiger (but not post-Tiger), so we shouldn't delete it
+// until the day comes when we're no longer supporting Mail on Tiger.
+- (WebFrame *)_frameForCurrentSelection
+{
+ return [self _selectedOrMainFrame];
+}
+
+- (void)setTabKeyCyclesThroughElements:(BOOL)cyclesElements
+{
+ _private->tabKeyCyclesThroughElementsChanged = YES;
+ if (_private->page)
+ _private->page->setTabKeyCyclesThroughElements(cyclesElements);
+}
+
+- (BOOL)tabKeyCyclesThroughElements
+{
+ return _private->page && _private->page->tabKeyCyclesThroughElements();
+}
+
+- (void)setScriptDebugDelegate:(id)delegate
+{
+ _private->scriptDebugDelegate = delegate;
+ [_private->scriptDebugDelegateForwarder release];
+ _private->scriptDebugDelegateForwarder = nil;
+ if (delegate)
+ [self _attachScriptDebuggerToAllFrames];
+ else
+ [self _detachScriptDebuggerFromAllFrames];
+}
+
+- (id)scriptDebugDelegate
+{
+ return _private->scriptDebugDelegate;
+}
+
+- (BOOL)shouldClose
+{
+ Frame* coreFrame = core([self mainFrame]);
+ if (!coreFrame)
+ return YES;
+ return coreFrame->shouldClose();
+}
+
+- (NSAppleEventDescriptor *)aeDescByEvaluatingJavaScriptFromString:(NSString *)script
+{
+ return [[[self mainFrame] _bridge] aeDescByEvaluatingJavaScriptFromString:script];
+}
+
+- (BOOL)canMarkAllTextMatches
+{
+ WebFrame *frame = [self mainFrame];
+ do {
+ id <WebDocumentView> view = [[frame frameView] documentView];
+ if (view && ![view conformsToProtocol:@protocol(WebMultipleTextMatches)])
+ return NO;
+
+ frame = incrementFrame(frame, YES, NO);
+ } while (frame);
+
+ return YES;
+}
+
+- (NSUInteger)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag highlight:(BOOL)highlight limit:(NSUInteger)limit
+{
+ WebFrame *frame = [self mainFrame];
+ unsigned matchCount = 0;
+ do {
+ id <WebDocumentView> view = [[frame frameView] documentView];
+ if ([view conformsToProtocol:@protocol(WebMultipleTextMatches)]) {
+ [(NSView <WebMultipleTextMatches>*)view setMarkedTextMatchesAreHighlighted:highlight];
+
+ ASSERT(limit == 0 || matchCount < limit);
+ matchCount += [(NSView <WebMultipleTextMatches>*)view markAllMatchesForText:string caseSensitive:caseFlag limit:limit == 0 ? 0 : limit - matchCount];
+
+ // Stop looking if we've reached the limit. A limit of 0 means no limit.
+ if (limit > 0 && matchCount >= limit)
+ break;
+ }
+
+ frame = incrementFrame(frame, YES, NO);
+ } while (frame);
+
+ return matchCount;
+}
+
+- (void)unmarkAllTextMatches
+{
+ WebFrame *frame = [self mainFrame];
+ do {
+ id <WebDocumentView> view = [[frame frameView] documentView];
+ if ([view conformsToProtocol:@protocol(WebMultipleTextMatches)])
+ [(NSView <WebMultipleTextMatches>*)view unmarkAllTextMatches];
+
+ frame = incrementFrame(frame, YES, NO);
+ } while (frame);
+}
+
+- (NSArray *)rectsForTextMatches
+{
+ NSMutableArray *result = [NSMutableArray array];
+ WebFrame *frame = [self mainFrame];
+ do {
+ id <WebDocumentView> view = [[frame frameView] documentView];
+ if ([view conformsToProtocol:@protocol(WebMultipleTextMatches)]) {
+ NSView <WebMultipleTextMatches> *documentView = (NSView <WebMultipleTextMatches> *)view;
+ NSRect documentViewVisibleRect = [documentView visibleRect];
+ NSArray *originalRects = [documentView rectsForTextMatches];
+ unsigned rectCount = [originalRects count];
+ unsigned rectIndex;
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ for (rectIndex = 0; rectIndex < rectCount; ++rectIndex) {
+ NSRect r = [[originalRects objectAtIndex:rectIndex] rectValue];
+ // Clip rect to document view's visible rect so rect is confined to subframe
+ r = NSIntersectionRect(r, documentViewVisibleRect);
+ if (NSIsEmptyRect(r))
+ continue;
+
+ // Convert rect to our coordinate system
+ r = [documentView convertRect:r toView:self];
+ [result addObject:[NSValue valueWithRect:r]];
+ if (rectIndex % 10 == 0) {
+ [pool drain];
+ pool = [[NSAutoreleasePool alloc] init];
+ }
+ }
+ [pool drain];
+ }
+
+ frame = incrementFrame(frame, YES, NO);
+ } while (frame);
+
+ return result;
+}
+
+- (void)scrollDOMRangeToVisible:(DOMRange *)range
+{
+ [[[range startContainer] _bridge] scrollDOMRangeToVisible:range];
+}
+
+- (BOOL)allowsUndo
+{
+ return _private->allowsUndo;
+}
+
+- (void)setAllowsUndo:(BOOL)flag
+{
+ _private->allowsUndo = flag;
+}
+
+@end
+
+@implementation WebView (WebViewPrintingPrivate)
+
+- (float)_headerHeight
+{
+ return CallUIDelegateReturningFloat(self, @selector(webViewHeaderHeight:));
+}
+
+- (float)_footerHeight
+{
+ return CallUIDelegateReturningFloat(self, @selector(webViewFooterHeight:));
+}
+
+- (void)_drawHeaderInRect:(NSRect)rect
+{
+#ifdef DEBUG_HEADER_AND_FOOTER
+ NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
+ [currentContext saveGraphicsState];
+ [[NSColor yellowColor] set];
+ NSRectFill(rect);
+ [currentContext restoreGraphicsState];
+#endif
+
+ SEL selector = @selector(webView:drawHeaderInRect:);
+ if (![_private->UIDelegate respondsToSelector:selector])
+ return;
+
+ NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
+ [currentContext saveGraphicsState];
+
+ NSRectClip(rect);
+ CallUIDelegate(self, selector, rect);
+
+ [currentContext restoreGraphicsState];
+}
+
+- (void)_drawFooterInRect:(NSRect)rect
+{
+#ifdef DEBUG_HEADER_AND_FOOTER
+ NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
+ [currentContext saveGraphicsState];
+ [[NSColor cyanColor] set];
+ NSRectFill(rect);
+ [currentContext restoreGraphicsState];
+#endif
+
+ SEL selector = @selector(webView:drawFooterInRect:);
+ if (![_private->UIDelegate respondsToSelector:selector])
+ return;
+
+ NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];
+ [currentContext saveGraphicsState];
+
+ NSRectClip(rect);
+ CallUIDelegate(self, selector, rect);
+
+ [currentContext restoreGraphicsState];
+}
+
+- (void)_adjustPrintingMarginsForHeaderAndFooter
+{
+ NSPrintOperation *op = [NSPrintOperation currentOperation];
+ NSPrintInfo *info = [op printInfo];
+ NSMutableDictionary *infoDictionary = [info dictionary];
+
+ // We need to modify the top and bottom margins in the NSPrintInfo to account for the space needed by the
+ // header and footer. Because this method can be called more than once on the same NSPrintInfo (see 5038087),
+ // we stash away the unmodified top and bottom margins the first time this method is called, and we read from
+ // those stashed-away values on subsequent calls.
+ float originalTopMargin;
+ float originalBottomMargin;
+ NSNumber *originalTopMarginNumber = [infoDictionary objectForKey:WebKitOriginalTopPrintingMarginKey];
+ if (!originalTopMarginNumber) {
+ ASSERT(![infoDictionary objectForKey:WebKitOriginalBottomPrintingMarginKey]);
+ originalTopMargin = [info topMargin];
+ originalBottomMargin = [info bottomMargin];
+ [infoDictionary setObject:[NSNumber numberWithFloat:originalTopMargin] forKey:WebKitOriginalTopPrintingMarginKey];
+ [infoDictionary setObject:[NSNumber numberWithFloat:originalBottomMargin] forKey:WebKitOriginalBottomPrintingMarginKey];
+ } else {
+ ASSERT([originalTopMarginNumber isKindOfClass:[NSNumber class]]);
+ ASSERT([[infoDictionary objectForKey:WebKitOriginalBottomPrintingMarginKey] isKindOfClass:[NSNumber class]]);
+ originalTopMargin = [originalTopMarginNumber floatValue];
+ originalBottomMargin = [[infoDictionary objectForKey:WebKitOriginalBottomPrintingMarginKey] floatValue];
+ }
+
+ float scale = [op _web_pageSetupScaleFactor];
+ [info setTopMargin:originalTopMargin + [self _headerHeight] * scale];
+ [info setBottomMargin:originalBottomMargin + [self _footerHeight] * scale];
+}
+
+- (void)_drawHeaderAndFooter
+{
+ // The header and footer rect height scales with the page, but the width is always
+ // all the way across the printed page (inset by printing margins).
+ NSPrintOperation *op = [NSPrintOperation currentOperation];
+ float scale = [op _web_pageSetupScaleFactor];
+ NSPrintInfo *printInfo = [op printInfo];
+ NSSize paperSize = [printInfo paperSize];
+ float headerFooterLeft = [printInfo leftMargin]/scale;
+ float headerFooterWidth = (paperSize.width - ([printInfo leftMargin] + [printInfo rightMargin]))/scale;
+ NSRect footerRect = NSMakeRect(headerFooterLeft, [printInfo bottomMargin]/scale - [self _footerHeight] ,
+ headerFooterWidth, [self _footerHeight]);
+ NSRect headerRect = NSMakeRect(headerFooterLeft, (paperSize.height - [printInfo topMargin])/scale,
+ headerFooterWidth, [self _headerHeight]);
+
+ [self _drawHeaderInRect:headerRect];
+ [self _drawFooterInRect:footerRect];
+}
+@end
+
+@implementation WebView (WebDebugBinding)
+
+- (void)addObserver:(NSObject *)anObserver forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
+{
+ LOG (Bindings, "addObserver:%p forKeyPath:%@ options:%x context:%p", anObserver, keyPath, options, context);
+ [super addObserver:anObserver forKeyPath:keyPath options:options context:context];
+}
+
+- (void)removeObserver:(NSObject *)anObserver forKeyPath:(NSString *)keyPath
+{
+ LOG (Bindings, "removeObserver:%p forKeyPath:%@", anObserver, keyPath);
+ [super removeObserver:anObserver forKeyPath:keyPath];
+}
+
+@end
+
+//==========================================================================================
+// Editing
+
+@implementation WebView (WebViewCSS)
+
+- (DOMCSSStyleDeclaration *)computedStyleForElement:(DOMElement *)element pseudoElement:(NSString *)pseudoElement
+{
+ // FIXME: is this the best level for this conversion?
+ if (pseudoElement == nil) {
+ pseudoElement = @"";
+ }
+ return [[element ownerDocument] getComputedStyle:element pseudoElement:pseudoElement];
+}
+
+@end
+
+@implementation WebView (WebViewEditing)
+
+- (DOMRange *)editableDOMRangeForPoint:(NSPoint)point
+{
+ Page* page = core(self);
+ if (!page)
+ return nil;
+ return kit(page->mainFrame()->editor()->rangeForPoint(IntPoint([self convertPoint:point toView:nil])).get());
+}
+
+- (BOOL)_shouldChangeSelectedDOMRange:(DOMRange *)currentRange toDOMRange:(DOMRange *)proposedRange affinity:(NSSelectionAffinity)selectionAffinity stillSelecting:(BOOL)flag;
+{
+ // FIXME: This quirk is needed due to <rdar://problem/4985321> - We can phase it out once Aperture can adopt the new behavior on their end
+ if (!WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_APERTURE_QUIRK) && [[[NSBundle mainBundle] bundleIdentifier] isEqualToString:@"com.apple.Aperture"])
+ return YES;
+ return [[self _editingDelegateForwarder] webView:self shouldChangeSelectedDOMRange:currentRange toDOMRange:proposedRange affinity:selectionAffinity stillSelecting:flag];
+}
+
+- (BOOL)maintainsInactiveSelection
+{
+ return NO;
+}
+
+- (void)setSelectedDOMRange:(DOMRange *)range affinity:(NSSelectionAffinity)selectionAffinity
+{
+ Frame* coreFrame = core([self _selectedOrMainFrame]);
+ if (!coreFrame)
+ return;
+
+ if (range == nil)
+ coreFrame->selectionController()->clear();
+ else {
+ // Derive the frame to use from the range passed in.
+ // Using _bridgeForSelectedOrMainFrame could give us a different document than
+ // the one the range uses.
+ coreFrame = core([range startContainer])->document()->frame();
+ if (!coreFrame)
+ return;
+
+ ExceptionCode ec = 0;
+ coreFrame->selectionController()->setSelectedRange([range _range], core(selectionAffinity), true, ec);
+ }
+}
+
+- (DOMRange *)selectedDOMRange
+{
+ Frame* coreFrame = core([self _selectedOrMainFrame]);
+ if (!coreFrame)
+ return nil;
+ return kit(coreFrame->selectionController()->toRange().get());
+}
+
+- (NSSelectionAffinity)selectionAffinity
+{
+ Frame* coreFrame = core([self _selectedOrMainFrame]);
+ if (!coreFrame)
+ return NSSelectionAffinityDownstream;
+ return kit(coreFrame->selectionController()->affinity());
+}
+
+- (void)setEditable:(BOOL)flag
+{
+ if (_private->editable != flag) {
+ _private->editable = flag;
+ if (!_private->tabKeyCyclesThroughElementsChanged && _private->page)
+ _private->page->setTabKeyCyclesThroughElements(!flag);
+ Frame* mainFrame = [[[self mainFrame] _bridge] _frame];
+ if (mainFrame) {
+ if (flag) {
+ mainFrame->applyEditingStyleToBodyElement();
+ // If the WebView is made editable and the selection is empty, set it to something.
+ if (![self selectedDOMRange])
+ mainFrame->setSelectionFromNone();
+ } else
+ mainFrame->removeEditingStyleFromBodyElement();
+ }
+ }
+}
+
+- (BOOL)isEditable
+{
+ return _private->editable;
+}
+
+- (void)setTypingStyle:(DOMCSSStyleDeclaration *)style
+{
+ // We don't know enough at thls level to pass in a relevant WebUndoAction; we'd have to
+ // change the API to allow this.
+ [[self _bridgeForSelectedOrMainFrame] setTypingStyle:style withUndoAction:EditActionUnspecified];
+}
+
+- (DOMCSSStyleDeclaration *)typingStyle
+{
+ return [[self _bridgeForSelectedOrMainFrame] typingStyle];
+}
+
+- (void)setSmartInsertDeleteEnabled:(BOOL)flag
+{
+ _private->smartInsertDeleteEnabled = flag;
+}
+
+- (BOOL)smartInsertDeleteEnabled
+{
+ return _private->smartInsertDeleteEnabled;
+}
+
+- (void)setContinuousSpellCheckingEnabled:(BOOL)flag
+{
+ if (continuousSpellCheckingEnabled != flag) {
+ continuousSpellCheckingEnabled = flag;
+ [[NSUserDefaults standardUserDefaults] setBool:continuousSpellCheckingEnabled forKey:WebContinuousSpellCheckingEnabled];
+ }
+
+ if ([self isContinuousSpellCheckingEnabled]) {
+ [[self class] _preflightSpellChecker];
+ } else {
+ [[self mainFrame] _unmarkAllMisspellings];
+ }
+}
+
+- (BOOL)isContinuousSpellCheckingEnabled
+{
+ return (continuousSpellCheckingEnabled && [self _continuousCheckingAllowed]);
+}
+
+- (NSInteger)spellCheckerDocumentTag
+{
+ if (!_private->hasSpellCheckerDocumentTag) {
+ _private->spellCheckerDocumentTag = [NSSpellChecker uniqueSpellDocumentTag];
+ _private->hasSpellCheckerDocumentTag = YES;
+ }
+ return _private->spellCheckerDocumentTag;
+}
+
+- (NSUndoManager *)undoManager
+{
+ if (!_private->allowsUndo)
+ return nil;
+
+ NSUndoManager *undoManager = [[self _editingDelegateForwarder] undoManagerForWebView:self];
+ if (undoManager)
+ return undoManager;
+
+ return [super undoManager];
+}
+
+- (void)registerForEditingDelegateNotification:(NSString *)name selector:(SEL)selector
+{
+ NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
+ if ([_private->editingDelegate respondsToSelector:selector])
+ [defaultCenter addObserver:_private->editingDelegate selector:selector name:name object:self];
+}
+
+- (void)setEditingDelegate:(id)delegate
+{
+ if (_private->editingDelegate == delegate)
+ return;
+
+ NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
+
+ // remove notifications from current delegate
+ [defaultCenter removeObserver:_private->editingDelegate name:WebViewDidBeginEditingNotification object:self];
+ [defaultCenter removeObserver:_private->editingDelegate name:WebViewDidChangeNotification object:self];
+ [defaultCenter removeObserver:_private->editingDelegate name:WebViewDidEndEditingNotification object:self];
+ [defaultCenter removeObserver:_private->editingDelegate name:WebViewDidChangeTypingStyleNotification object:self];
+ [defaultCenter removeObserver:_private->editingDelegate name:WebViewDidChangeSelectionNotification object:self];
+
+ _private->editingDelegate = delegate;
+ [_private->editingDelegateForwarder release];
+ _private->editingDelegateForwarder = nil;
+
+ // add notifications for new delegate
+ [self registerForEditingDelegateNotification:WebViewDidBeginEditingNotification selector:@selector(webViewDidBeginEditing:)];
+ [self registerForEditingDelegateNotification:WebViewDidChangeNotification selector:@selector(webViewDidChange:)];
+ [self registerForEditingDelegateNotification:WebViewDidEndEditingNotification selector:@selector(webViewDidEndEditing:)];
+ [self registerForEditingDelegateNotification:WebViewDidChangeTypingStyleNotification selector:@selector(webViewDidChangeTypingStyle:)];
+ [self registerForEditingDelegateNotification:WebViewDidChangeSelectionNotification selector:@selector(webViewDidChangeSelection:)];
+}
+
+- (id)editingDelegate
+{
+ return _private->editingDelegate;
+}
+
+- (DOMCSSStyleDeclaration *)styleDeclarationWithText:(NSString *)text
+{
+ // FIXME: Should this really be attached to the document with the current selection?
+ DOMCSSStyleDeclaration *decl = [[[self _selectedOrMainFrame] DOMDocument] createCSSStyleDeclaration];
+ [decl setCssText:text];
+ return decl;
+}
+
+@end
+
+@implementation WebView (WebViewGrammarChecking)
+
+// FIXME: This method should be merged into WebViewEditing when we're not in API freeze
+- (BOOL)isGrammarCheckingEnabled
+{
+#ifdef BUILDING_ON_TIGER
+ return NO;
+#else
+ return grammarCheckingEnabled;
+#endif
+}
+
+#ifndef BUILDING_ON_TIGER
+// FIXME: This method should be merged into WebViewEditing when we're not in API freeze
+- (void)setGrammarCheckingEnabled:(BOOL)flag
+{
+ if (grammarCheckingEnabled == flag)
+ return;
+
+ grammarCheckingEnabled = flag;
+ [[NSUserDefaults standardUserDefaults] setBool:grammarCheckingEnabled forKey:WebGrammarCheckingEnabled];
+
+ // FIXME 4811447: workaround for lack of API
+ NSSpellChecker *spellChecker = [NSSpellChecker sharedSpellChecker];
+ if ([spellChecker respondsToSelector:@selector(_updateGrammar)])
+ [spellChecker performSelector:@selector(_updateGrammar)];
+
+ // We call _preflightSpellChecker when turning continuous spell checking on, but we don't need to do that here
+ // because grammar checking only occurs on code paths that already preflight spell checking appropriately.
+
+ if (![self isGrammarCheckingEnabled])
+ [[self mainFrame] _unmarkAllBadGrammar];
+}
+
+// FIXME: This method should be merged into WebIBActions when we're not in API freeze
+- (void)toggleGrammarChecking:(id)sender
+{
+ [self setGrammarCheckingEnabled:![self isGrammarCheckingEnabled]];
+}
+#endif
+
+@end
+
+@implementation WebView (WebViewUndoableEditing)
+
+- (void)replaceSelectionWithNode:(DOMNode *)node
+{
+ [[self _bridgeForSelectedOrMainFrame] replaceSelectionWithNode:node selectReplacement:YES smartReplace:NO matchStyle:NO];
+}
+
+- (void)replaceSelectionWithText:(NSString *)text
+{
+ [[self _bridgeForSelectedOrMainFrame] replaceSelectionWithText:text selectReplacement:YES smartReplace:NO];
+}
+
+- (void)replaceSelectionWithMarkupString:(NSString *)markupString
+{
+ [[self _bridgeForSelectedOrMainFrame] replaceSelectionWithMarkupString:markupString baseURLString:nil selectReplacement:YES smartReplace:NO];
+}
+
+- (void)replaceSelectionWithArchive:(WebArchive *)archive
+{
+ [[[[self _bridgeForSelectedOrMainFrame] webFrame] _dataSource] _replaceSelectionWithArchive:archive selectReplacement:YES];
+}
+
+- (void)deleteSelection
+{
+ WebFrame *webFrame = [self _selectedOrMainFrame];
+ Frame* coreFrame = core(webFrame);
+ if (coreFrame)
+ coreFrame->editor()->deleteSelectionWithSmartDelete([(WebHTMLView *)[[webFrame frameView] documentView] _canSmartCopyOrDelete]);
+}
+
+- (void)applyStyle:(DOMCSSStyleDeclaration *)style
+{
+ // We don't know enough at thls level to pass in a relevant WebUndoAction; we'd have to
+ // change the API to allow this.
+ WebFrame *webFrame = [self _selectedOrMainFrame];
+ Frame* coreFrame = core(webFrame);
+ if (coreFrame)
+ coreFrame->editor()->applyStyle(core(style));
+}
+
+@end
+
+@implementation WebView (WebViewEditingActions)
+
+- (void)_performResponderOperation:(SEL)selector with:(id)parameter
+{
+ static BOOL reentered = NO;
+ if (reentered) {
+ [[self nextResponder] tryToPerform:selector with:parameter];
+ return;
+ }
+
+ // There are two possibilities here.
+ //
+ // One is that WebView has been called in its role as part of the responder chain.
+ // In that case, it's fine to call the first responder and end up calling down the
+ // responder chain again. Later we will return here with reentered = YES and continue
+ // past the WebView.
+ //
+ // The other is that we are being called directly, in which case we want to pass the
+ // selector down to the view inside us that can handle it, and continue down the
+ // responder chain as usual.
+
+ // Pass this selector down to the first responder.
+ NSResponder *responder = [self _responderForResponderOperations];
+ reentered = YES;
+ [responder tryToPerform:selector with:parameter];
+ reentered = NO;
+}
+
+#define FORWARD(name) \
+ - (void)name:(id)sender { [self _performResponderOperation:_cmd with:sender]; }
+
+FOR_EACH_RESPONDER_SELECTOR(FORWARD)
+
+- (void)insertText:(NSString *)text
+{
+ [self _performResponderOperation:_cmd with:text];
+}
+
+@end
+
+@implementation WebView (WebViewEditingInMail)
+
+- (void)_insertNewlineInQuotedContent;
+{
+ [[self _bridgeForSelectedOrMainFrame] insertParagraphSeparatorInQuotedContent];
+}
+
+- (void)_replaceSelectionWithNode:(DOMNode *)node matchStyle:(BOOL)matchStyle
+{
+ [[self _bridgeForSelectedOrMainFrame] replaceSelectionWithNode:node selectReplacement:YES smartReplace:NO matchStyle:matchStyle];
+}
+
+@end
+
+static WebFrameView *containingFrameView(NSView *view)
+{
+ while (view && ![view isKindOfClass:[WebFrameView class]])
+ view = [view superview];
+ return (WebFrameView *)view;
+}
+
+@implementation WebView (WebFileInternal)
+
++ (void)_setCacheModel:(WebCacheModel)cacheModel
+{
+ if (s_didSetCacheModel && cacheModel == s_cacheModel)
+ return;
+
+ NSString *nsurlCacheDirectory = [(NSString *)WKCopyFoundationCacheDirectory() autorelease];
+ if (!nsurlCacheDirectory)
+ nsurlCacheDirectory = NSHomeDirectory();
+
+ // As a fudge factor, use 1000 instead of 1024, in case the reported byte
+ // count doesn't align exactly to a megabyte boundary.
+ vm_size_t memSize = WebMemorySize() / 1024 / 1000;
+ unsigned long long diskFreeSize = WebVolumeFreeSize(nsurlCacheDirectory) / 1024 / 1000;
+ NSURLCache *nsurlCache = [NSURLCache sharedURLCache];
+
+ unsigned cacheTotalCapacity = 0;
+ unsigned cacheMinDeadCapacity = 0;
+ unsigned cacheMaxDeadCapacity = 0;
+
+ unsigned pageCacheCapacity = 0;
+
+ NSUInteger nsurlCacheMemoryCapacity = 0;
+ NSUInteger nsurlCacheDiskCapacity = 0;
+
+ switch (cacheModel) {
+ case WebCacheModelDocumentViewer: {
+ // Page cache capacity (in pages)
+ pageCacheCapacity = 0;
+
+ // Object cache capacities (in bytes)
+ if (memSize >= 4096)
+ cacheTotalCapacity = 256 * 1024 * 1024;
+ else if (memSize >= 3072)
+ cacheTotalCapacity = 192 * 1024 * 1024;
+ else if (memSize >= 2048)
+ cacheTotalCapacity = 128 * 1024 * 1024;
+ else if (memSize >= 1536)
+ cacheTotalCapacity = 86 * 1024 * 1024;
+ else if (memSize >= 1024)
+ cacheTotalCapacity = 64 * 1024 * 1024;
+ else if (memSize >= 512)
+ cacheTotalCapacity = 32 * 1024 * 1024;
+ else if (memSize >= 256)
+ cacheTotalCapacity = 16 * 1024 * 1024;
+
+ cacheMinDeadCapacity = 0;
+ cacheMaxDeadCapacity = 0;
+
+ // Foundation memory cache capacity (in bytes)
+ nsurlCacheMemoryCapacity = 0;
+
+ // Foundation disk cache capacity (in bytes)
+ nsurlCacheDiskCapacity = [nsurlCache diskCapacity];
+
+ break;
+ }
+ case WebCacheModelDocumentBrowser: {
+ // Page cache capacity (in pages)
+ if (memSize >= 1024)
+ pageCacheCapacity = 3;
+ else if (memSize >= 512)
+ pageCacheCapacity = 2;
+ else if (memSize >= 256)
+ pageCacheCapacity = 1;
+ else
+ pageCacheCapacity = 0;
+
+ // Object cache capacities (in bytes)
+ if (memSize >= 4096)
+ cacheTotalCapacity = 256 * 1024 * 1024;
+ else if (memSize >= 3072)
+ cacheTotalCapacity = 192 * 1024 * 1024;
+ else if (memSize >= 2048)
+ cacheTotalCapacity = 128 * 1024 * 1024;
+ else if (memSize >= 1536)
+ cacheTotalCapacity = 86 * 1024 * 1024;
+ else if (memSize >= 1024)
+ cacheTotalCapacity = 64 * 1024 * 1024;
+ else if (memSize >= 512)
+ cacheTotalCapacity = 32 * 1024 * 1024;
+ else if (memSize >= 256)
+ cacheTotalCapacity = 16 * 1024 * 1024;
+
+ cacheMinDeadCapacity = cacheTotalCapacity / 8;
+ cacheMaxDeadCapacity = cacheTotalCapacity / 4;
+
+ // Foundation memory cache capacity (in bytes)
+ if (memSize >= 2048)
+ nsurlCacheMemoryCapacity = 4 * 1024 * 1024;
+ else if (memSize >= 1024)
+ nsurlCacheMemoryCapacity = 2 * 1024 * 1024;
+ else if (memSize >= 512)
+ nsurlCacheMemoryCapacity = 1 * 1024 * 1024;
+ else
+ nsurlCacheMemoryCapacity = 512 * 1024;
+
+ // Foundation disk cache capacity (in bytes)
+ if (diskFreeSize >= 16384)
+ nsurlCacheDiskCapacity = 50 * 1024 * 1024;
+ else if (diskFreeSize >= 8192)
+ nsurlCacheDiskCapacity = 40 * 1024 * 1024;
+ else if (diskFreeSize >= 4096)
+ nsurlCacheDiskCapacity = 30 * 1024 * 1024;
+ else
+ nsurlCacheDiskCapacity = 20 * 1024 * 1024;
+
+ break;
+ }
+ case WebCacheModelPrimaryWebBrowser: {
+ // Page cache capacity (in pages)
+ // (Research indicates that value / page drops substantially after 3 pages.)
+ if (memSize >= 8192)
+ pageCacheCapacity = 7;
+ if (memSize >= 4096)
+ pageCacheCapacity = 6;
+ else if (memSize >= 2048)
+ pageCacheCapacity = 5;
+ else if (memSize >= 1024)
+ pageCacheCapacity = 4;
+ else if (memSize >= 512)
+ pageCacheCapacity = 3;
+ else if (memSize >= 256)
+ pageCacheCapacity = 2;
+ else
+ pageCacheCapacity = 1;
+
+ // Object cache capacities (in bytes)
+ // (Testing indicates that value / MB depends heavily on content and
+ // browsing pattern. Even growth above 128MB can have substantial
+ // value / MB for some content / browsing patterns.)
+ if (memSize >= 4096)
+ cacheTotalCapacity = 512 * 1024 * 1024;
+ else if (memSize >= 3072)
+ cacheTotalCapacity = 384 * 1024 * 1024;
+ else if (memSize >= 2048)
+ cacheTotalCapacity = 256 * 1024 * 1024;
+ else if (memSize >= 1536)
+ cacheTotalCapacity = 172 * 1024 * 1024;
+ else if (memSize >= 1024)
+ cacheTotalCapacity = 128 * 1024 * 1024;
+ else if (memSize >= 512)
+ cacheTotalCapacity = 64 * 1024 * 1024;
+ else if (memSize >= 256)
+ cacheTotalCapacity = 32 * 1024 * 1024;
+
+ cacheMinDeadCapacity = cacheTotalCapacity / 4;
+ cacheMaxDeadCapacity = cacheTotalCapacity / 2;
+
+ // This code is here to avoid a PLT regression. We can remove it if we
+ // can prove that the overall system gain would justify the regression.
+ cacheMaxDeadCapacity = max(24u, cacheMaxDeadCapacity);
+
+ // Foundation memory cache capacity (in bytes)
+ // (These values are small because WebCore does most caching itself.)
+ if (memSize >= 1024)
+ nsurlCacheMemoryCapacity = 4 * 1024 * 1024;
+ else if (memSize >= 512)
+ nsurlCacheMemoryCapacity = 2 * 1024 * 1024;
+ else if (memSize >= 256)
+ nsurlCacheMemoryCapacity = 1 * 1024 * 1024;
+ else
+ nsurlCacheMemoryCapacity = 512 * 1024;
+
+ // Foundation disk cache capacity (in bytes)
+ if (diskFreeSize >= 16384)
+ nsurlCacheDiskCapacity = 175 * 1024 * 1024;
+ else if (diskFreeSize >= 8192)
+ nsurlCacheDiskCapacity = 150 * 1024 * 1024;
+ else if (diskFreeSize >= 4096)
+ nsurlCacheDiskCapacity = 125 * 1024 * 1024;
+ else if (diskFreeSize >= 2048)
+ nsurlCacheDiskCapacity = 100 * 1024 * 1024;
+ else if (diskFreeSize >= 1024)
+ nsurlCacheDiskCapacity = 75 * 1024 * 1024;
+ else
+ nsurlCacheDiskCapacity = 50 * 1024 * 1024;
+
+ break;
+ }
+ default:
+ ASSERT_NOT_REACHED();
+ };
+
+#ifdef BUILDING_ON_TIGER
+ // Don't use a big Foundation disk cache on Tiger because, according to the
+ // PLT, the Foundation disk cache on Tiger is slower than the network.
+ nsurlCacheDiskCapacity = [[NSURLCache sharedURLCache] diskCapacity];
+#else
+ // Don't use a big Foundation disk cache on older versions of Leopard because
+ // doing so causes a SPOD on launch (<rdar://problem/5465260>).
+ if (NSVersionOfRunTimeLibrary("CFNetwork") < WEBKIT_FIRST_CFNETWORK_VERSION_WITH_LARGE_DISK_CACHE_FIX)
+ nsurlCacheDiskCapacity = [[NSURLCache sharedURLCache] diskCapacity];
+#endif
+
+ // Don't shrink a big disk cache, since that would cause churn.
+ nsurlCacheDiskCapacity = max(nsurlCacheDiskCapacity, [nsurlCache diskCapacity]);
+
+ cache()->setCapacities(cacheMinDeadCapacity, cacheMaxDeadCapacity, cacheTotalCapacity);
+ pageCache()->setCapacity(pageCacheCapacity);
+ [nsurlCache setMemoryCapacity:nsurlCacheMemoryCapacity];
+ [nsurlCache setDiskCapacity:nsurlCacheDiskCapacity];
+
+ s_cacheModel = cacheModel;
+ s_didSetCacheModel = YES;
+}
+
++ (WebCacheModel)_cacheModel
+{
+ return s_cacheModel;
+}
+
++ (WebCacheModel)_didSetCacheModel
+{
+ return s_didSetCacheModel;
+}
+
++ (WebCacheModel)_maxCacheModelInAnyInstance
+{
+ WebCacheModel cacheModel = WebCacheModelDocumentViewer;
+ NSEnumerator *enumerator = [(NSMutableSet *)allWebViewsSet objectEnumerator];
+ while (WebPreferences *preferences = [[enumerator nextObject] preferences])
+ cacheModel = max(cacheModel, [preferences cacheModel]);
+ return cacheModel;
+}
+
++ (void)_preferencesChangedNotification:(NSNotification *)notification
+{
+ WebPreferences *preferences = (WebPreferences *)[notification object];
+ ASSERT([preferences isKindOfClass:[WebPreferences class]]);
+
+ WebCacheModel cacheModel = [preferences cacheModel];
+ if (![self _didSetCacheModel] || cacheModel > [self _cacheModel])
+ [self _setCacheModel:cacheModel];
+ else if (cacheModel < [self _cacheModel])
+ [self _setCacheModel:max([[WebPreferences standardPreferences] cacheModel], [self _maxCacheModelInAnyInstance])];
+}
+
++ (void)_preferencesRemovedNotification:(NSNotification *)notification
+{
+ WebPreferences *preferences = (WebPreferences *)[notification object];
+ ASSERT([preferences isKindOfClass:[WebPreferences class]]);
+
+ if ([preferences cacheModel] == [self _cacheModel])
+ [self _setCacheModel:max([[WebPreferences standardPreferences] cacheModel], [self _maxCacheModelInAnyInstance])];
+}
+
+- (WebFrame *)_focusedFrame
+{
+ NSResponder *resp = [[self window] firstResponder];
+ if (resp && [resp isKindOfClass:[NSView class]] && [(NSView *)resp isDescendantOf:[[self mainFrame] frameView]]) {
+ WebFrameView *frameView = containingFrameView((NSView *)resp);
+ ASSERT(frameView != nil);
+ return [frameView webFrame];
+ }
+
+ return nil;
+}
+
+- (WebFrame *)_selectedOrMainFrame
+{
+ WebFrame *result = [self selectedFrame];
+ if (result == nil)
+ result = [self mainFrame];
+ return result;
+}
+
+- (WebFrameBridge *)_bridgeForSelectedOrMainFrame
+{
+ return [[self _selectedOrMainFrame] _bridge];
+}
+
+- (BOOL)_isLoading
+{
+ WebFrame *mainFrame = [self mainFrame];
+ return [[mainFrame _dataSource] isLoading]
+ || [[mainFrame provisionalDataSource] isLoading];
+}
+
+- (WebFrameView *)_frameViewAtWindowPoint:(NSPoint)point
+{
+ if (_private->closed)
+ return nil;
+ NSView *view = [self hitTest:[[self superview] convertPoint:point fromView:nil]];
+ if (![view isDescendantOf:[[self mainFrame] frameView]])
+ return nil;
+ WebFrameView *frameView = containingFrameView(view);
+ ASSERT(frameView);
+ return frameView;
+}
+
++ (void)_preflightSpellCheckerNow:(id)sender
+{
+ [[NSSpellChecker sharedSpellChecker] _preflightChosenSpellServer];
+}
+
++ (void)_preflightSpellChecker
+{
+ // As AppKit does, we wish to delay tickling the shared spellchecker into existence on application launch.
+ if ([NSSpellChecker sharedSpellCheckerExists]) {
+ [self _preflightSpellCheckerNow:self];
+ } else {
+ [self performSelector:@selector(_preflightSpellCheckerNow:) withObject:self afterDelay:2.0];
+ }
+}
+
+- (BOOL)_continuousCheckingAllowed
+{
+ static BOOL allowContinuousSpellChecking = YES;
+ static BOOL readAllowContinuousSpellCheckingDefault = NO;
+ if (!readAllowContinuousSpellCheckingDefault) {
+ if ([[NSUserDefaults standardUserDefaults] objectForKey:@"NSAllowContinuousSpellChecking"]) {
+ allowContinuousSpellChecking = [[NSUserDefaults standardUserDefaults] boolForKey:@"NSAllowContinuousSpellChecking"];
+ }
+ readAllowContinuousSpellCheckingDefault = YES;
+ }
+ return allowContinuousSpellChecking;
+}
+
+- (NSResponder *)_responderForResponderOperations
+{
+ NSResponder *responder = [[self window] firstResponder];
+ WebFrameView *mainFrameView = [[self mainFrame] frameView];
+
+ // If the current responder is outside of the webview, use our main frameView or its
+ // document view. We also do this for subviews of self that are siblings of the main
+ // frameView since clients might insert non-webview-related views there (see 4552713).
+ if (responder != self && ![mainFrameView _web_firstResponderIsSelfOrDescendantView]) {
+ responder = [mainFrameView documentView];
+ if (!responder)
+ responder = mainFrameView;
+ }
+ return responder;
+}
+
+- (void)_openFrameInNewWindowFromMenu:(NSMenuItem *)sender
+{
+ ASSERT_ARG(sender, [sender isKindOfClass:[NSMenuItem class]]);
+
+ NSDictionary *element = [sender representedObject];
+ ASSERT([element isKindOfClass:[NSDictionary class]]);
+
+ NSURLRequest *request = [[[[element objectForKey:WebElementFrameKey] dataSource] request] copy];
+ ASSERT(request);
+
+ [self _openNewWindowWithRequest:request];
+ [request release];
+}
+
+- (void)_searchWithGoogleFromMenu:(id)sender
+{
+ id documentView = [[[self selectedFrame] frameView] documentView];
+ if (![documentView conformsToProtocol:@protocol(WebDocumentText)]) {
+ return;
+ }
+
+ NSString *selectedString = [(id <WebDocumentText>)documentView selectedString];
+ if ([selectedString length] == 0) {
+ return;
+ }
+
+ NSPasteboard *pasteboard = [NSPasteboard pasteboardWithUniqueName];
+ [pasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
+ NSMutableString *s = [selectedString mutableCopy];
+ const unichar nonBreakingSpaceCharacter = 0xA0;
+ NSString *nonBreakingSpaceString = [NSString stringWithCharacters:&nonBreakingSpaceCharacter length:1];
+ [s replaceOccurrencesOfString:nonBreakingSpaceString withString:@" " options:0 range:NSMakeRange(0, [s length])];
+ [pasteboard setString:s forType:NSStringPboardType];
+ [s release];
+
+ // FIXME: seems fragile to use the service by name, but this is what AppKit does
+ NSPerformService(@"Search With Google", pasteboard);
+}
+
+- (void)_searchWithSpotlightFromMenu:(id)sender
+{
+ id documentView = [[[self selectedFrame] frameView] documentView];
+ if (![documentView conformsToProtocol:@protocol(WebDocumentText)]) {
+ return;
+ }
+
+ NSString *selectedString = [(id <WebDocumentText>)documentView selectedString];
+ if ([selectedString length] == 0) {
+ return;
+ }
+
+ (void)HISearchWindowShow((CFStringRef)selectedString, kNilOptions);
+}
+
+// Slightly funky method that lets us have one copy of the logic for finding docViews that can do
+// text sizing. It returns whether it found any "suitable" doc views. It sends sel to any suitable
+// doc views, or if sel==0 we do nothing to them. For doc views that track our size factor, they are
+// suitable if doTrackingViews==YES (which in practice means that our size factor isn't at its max or
+// min). For doc views that don't track it, we send them testSel to determine suitablility. If we
+// do find any suitable tracking doc views and newScaleFactor!=0, we will set the common scale factor
+// to that new factor before we send sel to any of them.
+- (BOOL)_performTextSizingSelector:(SEL)sel withObject:(id)arg onTrackingDocs:(BOOL)doTrackingViews selForNonTrackingDocs:(SEL)testSel newScaleFactor:(float)newScaleFactor
+{
+ if ([[self mainFrame] _dataSource] == nil)
+ return NO;
+
+ BOOL foundSome = NO;
+ NSArray *docViews = [[self mainFrame] _documentViews];
+ for (int i = [docViews count]-1; i >= 0; i--) {
+ id docView = [docViews objectAtIndex:i];
+ if ([docView conformsToProtocol:@protocol(_WebDocumentTextSizing)]) {
+ id <_WebDocumentTextSizing> sizingDocView = (id <_WebDocumentTextSizing>)docView;
+ BOOL isSuitable;
+ if ([sizingDocView _tracksCommonSizeFactor]) {
+ isSuitable = doTrackingViews;
+ if (isSuitable && newScaleFactor != 0)
+ _private->textSizeMultiplier = newScaleFactor;
+ } else {
+ // Incantation to perform a selector returning a BOOL.
+ isSuitable = ((BOOL(*)(id, SEL))objc_msgSend)(sizingDocView, testSel);
+ }
+
+ if (isSuitable) {
+ if (sel != 0) {
+ foundSome = YES;
+ [sizingDocView performSelector:sel withObject:arg];
+ } else {
+ // if we're just called for the benefit of the return value, we can return at first match
+ return YES;
+ }
+ }
+ }
+ }
+
+ return foundSome;
+}
+
+- (void)_notifyTextSizeMultiplierChanged
+{
+ if ([[self mainFrame] _dataSource] == nil)
+ return;
+
+ NSArray *docViews = [[self mainFrame] _documentViews];
+ for (int i = [docViews count]-1; i >= 0; i--) {
+ id docView = [docViews objectAtIndex:i];
+ if ([docView conformsToProtocol:@protocol(_WebDocumentTextSizing)] == NO)
+ continue;
+
+ id <_WebDocumentTextSizing> sizingDocView = (id <_WebDocumentTextSizing>)docView;
+ if ([sizingDocView _tracksCommonSizeFactor])
+ [sizingDocView _textSizeMultiplierChanged];
+ }
+
+}
+
+@end
+
+@implementation WebView (WebViewInternal)
+
+- (BOOL)_becomingFirstResponderFromOutside
+{
+ return _private->becomingFirstResponderFromOutside;
+}
+
+- (void)_receivedIconChangedNotification:(NSNotification *)notification
+{
+ // Get the URL for this notification
+ NSDictionary *userInfo = [notification userInfo];
+ ASSERT([userInfo isKindOfClass:[NSDictionary class]]);
+ NSString *urlString = [userInfo objectForKey:WebIconNotificationUserInfoURLKey];
+ ASSERT([urlString isKindOfClass:[NSString class]]);
+
+ // If that URL matches the current main frame, dispatch the delegate call, which will also unregister
+ // us for this notification
+ if ([[self mainFrameURL] isEqualTo:urlString])
+ [self _dispatchDidReceiveIconFromWebFrame:[self mainFrame]];
+}
+
+- (void)_registerForIconNotification:(BOOL)listen
+{
+ if (listen)
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_receivedIconChangedNotification:) name:WebIconDatabaseDidAddIconNotification object:nil];
+ else
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:WebIconDatabaseDidAddIconNotification object:nil];
+}
+
+- (void)_dispatchDidReceiveIconFromWebFrame:(WebFrame *)webFrame
+{
+ // FIXME: This willChangeValueForKey call is too late, because the icon has already changed by now.
+ [self _willChangeValueForKey:_WebMainFrameIconKey];
+
+ // Since we definitely have an icon and are about to send out the delegate call for that, this WebView doesn't need to listen for the general
+ // notification any longer
+ [self _registerForIconNotification:NO];
+
+ WebFrameLoadDelegateImplementationCache implementations = WebViewGetFrameLoadDelegateImplementations(self);
+ if (implementations.didReceiveIconForFrameFunc) {
+ Image* image = iconDatabase()->iconForPageURL(core(webFrame)->loader()->url().url(), IntSize(16, 16));
+ if (NSImage *icon = webGetNSImage(image, NSMakeSize(16, 16)))
+ CallFrameLoadDelegate(implementations.didReceiveIconForFrameFunc, self, @selector(webView:didReceiveIcon:forFrame:), icon, webFrame);
+ }
+
+ [self _didChangeValueForKey:_WebMainFrameIconKey];
+}
+
+- (NSString *)_userVisibleBundleVersionFromFullVersion:(NSString *)fullVersion
+{
+ // If the version is 4 digits long or longer, then the first digit represents
+ // the version of the OS. Our user agent string should not include this first digit,
+ // so strip it off and report the rest as the version. <rdar://problem/4997547>
+ NSRange nonDigitRange = [fullVersion rangeOfCharacterFromSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]];
+ if (nonDigitRange.location == NSNotFound && [fullVersion length] >= 4)
+ return [fullVersion substringFromIndex:1];
+ if (nonDigitRange.location != NSNotFound && nonDigitRange.location >= 4)
+ return [fullVersion substringFromIndex:1];
+ return fullVersion;
+}
+
+- (NSString *)_userAgentWithApplicationName:(NSString *)applicationName andWebKitVersion:(NSString *)version
+{
+ NSString *language = [NSUserDefaults _webkit_preferredLanguageCode];
+ if ([applicationName length])
+ return [NSString stringWithFormat:@"Mozilla/5.0 (Macintosh; U; " PROCESSOR " Mac OS X; %@) AppleWebKit/%@ (KHTML, like Gecko) %@", language, version, applicationName];
+ return [NSString stringWithFormat:@"Mozilla/5.0 (Macintosh; U; " PROCESSOR " Mac OS X; %@) AppleWebKit/%@ (KHTML, like Gecko)", language, version];
+}
+
+// Get the appropriate user-agent string for a particular URL.
+- (WebCore::String)_userAgentForURL:(const WebCore::KURL&)url
+{
+ if (_private->useSiteSpecificSpoofing) {
+ // FIXME: Make this a hash table lookup if more domains need spoofing.
+ // FIXME: Remove yahoo.com once <rdar://problem/5057117> is fixed.
+ if (url.host().endsWith("yahoo.com")) {
+ static String yahooUserAgent([self _userAgentWithApplicationName:_private->applicationNameForUserAgent andWebKitVersion:@"422"]);
+ return yahooUserAgent;
+ }
+
+ // FIXME: Remove flickr.com workaround once <rdar://problem/5084872> is fixed
+ if (url.host().endsWith("flickr.com")) {
+ // Safari 2.0.4's user agent string works here
+ static String safari204UserAgent([self _userAgentWithApplicationName:@"Safari/419.3" andWebKitVersion:@"419"]);
+ return safari204UserAgent;
+ }
+ }
+
+ if (_private->userAgent->isNull()) {
+ NSString *sourceVersion = [[NSBundle bundleForClass:[WebView class]] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey];
+ sourceVersion = [self _userVisibleBundleVersionFromFullVersion:sourceVersion];
+ *_private->userAgent = [self _userAgentWithApplicationName:_private->applicationNameForUserAgent andWebKitVersion:sourceVersion];
+ }
+
+ return *_private->userAgent;
+}
+
+- (void)_addObject:(id)object forIdentifier:(unsigned long)identifier
+{
+ ASSERT(!_private->identifierMap->contains(identifier));
+
+ // If the identifier map is initially empty it means we're starting a load
+ // of something. The semantic is that the web view should be around as long
+ // as something is loading. Because of that we retain the web view.
+ if (_private->identifierMap->isEmpty())
+ CFRetain(self);
+
+ _private->identifierMap->set(identifier, object);
+}
+
+- (id)_objectForIdentifier:(unsigned long)identifier
+{
+ return _private->identifierMap->get(identifier).get();
+}
+
+- (void)_removeObjectForIdentifier:(unsigned long)identifier
+{
+ HashMap<unsigned long, RetainPtr<id> >::iterator it = _private->identifierMap->find(identifier);
+
+ // FIXME: This is currently needed because of a bug that causes didFail to be sent twice
+ // sometimes, see <rdar://problem/5009627> for more information.
+ if (it == _private->identifierMap->end())
+ return;
+
+ _private->identifierMap->remove(it);
+
+ // If the identifier map is now empty it means we're no longer loading anything
+ // and we should release the web view.
+ if (_private->identifierMap->isEmpty())
+ CFRelease(self);
+}
+
+@end
+
+// We use these functions to call the delegates and block exceptions. These functions are
+// declared inside a WebView category to get direct access to the delegate data memebers,
+// preventing more ObjC message dispatch and compensating for the expense of the @try/@catch.
+
+@implementation WebView (WebCallDelegateFunctions)
+
+#if !(defined(__i386__) || defined(__x86_64__))
+typedef double (*ObjCMsgSendFPRet)(id, SEL, ...);
+static const ObjCMsgSendFPRet objc_msgSend_fpret = reinterpret_cast<ObjCMsgSendFPRet>(objc_msgSend);
+#endif
+
+static inline id CallDelegate(WebView *self, id delegate, SEL selector)
+{
+ if (!delegate || ![delegate respondsToSelector:selector])
+ return nil;
+ if (!self->_private->catchesDelegateExceptions)
+ return objc_msgSend(delegate, selector, self);
+ @try {
+ return objc_msgSend(delegate, selector, self);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return nil;
+}
+
+static inline id CallDelegate(WebView *self, id delegate, SEL selector, id object)
+{
+ if (!delegate || ![delegate respondsToSelector:selector])
+ return nil;
+ if (!self->_private->catchesDelegateExceptions)
+ return objc_msgSend(delegate, selector, self, object);
+ @try {
+ return objc_msgSend(delegate, selector, self, object);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return nil;
+}
+
+static inline id CallDelegate(WebView *self, id delegate, SEL selector, NSRect rect)
+{
+ if (!delegate || ![delegate respondsToSelector:selector])
+ return nil;
+ if (!self->_private->catchesDelegateExceptions)
+ return reinterpret_cast<id (*)(id, SEL, WebView *, NSRect)>(objc_msgSend)(delegate, selector, self, rect);
+ @try {
+ return reinterpret_cast<id (*)(id, SEL, WebView *, NSRect)>(objc_msgSend)(delegate, selector, self, rect);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return nil;
+}
+
+static inline id CallDelegate(WebView *self, id delegate, SEL selector, id object1, id object2)
+{
+ if (!delegate || ![delegate respondsToSelector:selector])
+ return nil;
+ if (!self->_private->catchesDelegateExceptions)
+ return objc_msgSend(delegate, selector, self, object1, object2);
+ @try {
+ return objc_msgSend(delegate, selector, self, object1, object2);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return nil;
+}
+
+static inline id CallDelegate(WebView *self, id delegate, SEL selector, id object, BOOL boolean)
+{
+ if (!delegate || ![delegate respondsToSelector:selector])
+ return nil;
+ if (!self->_private->catchesDelegateExceptions)
+ return objc_msgSend(delegate, selector, self, object, boolean);
+ @try {
+ return objc_msgSend(delegate, selector, self, object, boolean);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return nil;
+}
+
+static inline id CallDelegate(WebView *self, id delegate, SEL selector, id object1, id object2, id object3)
+{
+ if (!delegate || ![delegate respondsToSelector:selector])
+ return nil;
+ if (!self->_private->catchesDelegateExceptions)
+ return objc_msgSend(delegate, selector, self, object1, object2, object3);
+ @try {
+ return objc_msgSend(delegate, selector, self, object1, object2, object3);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return nil;
+}
+
+static inline id CallDelegate(WebView *self, id delegate, SEL selector, id object, NSUInteger integer)
+{
+ if (!delegate || ![delegate respondsToSelector:selector])
+ return nil;
+ if (!self->_private->catchesDelegateExceptions)
+ return objc_msgSend(delegate, selector, self, object, integer);
+ @try {
+ return objc_msgSend(delegate, selector, self, object, integer);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return nil;
+}
+
+static inline float CallDelegateReturningFloat(WebView *self, id delegate, SEL selector)
+{
+ if (!delegate || ![delegate respondsToSelector:selector])
+ return 0.0f;
+ if (!self->_private->catchesDelegateExceptions)
+ return static_cast<float>(objc_msgSend_fpret(delegate, selector, self));
+ @try {
+ return static_cast<float>(objc_msgSend_fpret(delegate, selector, self));
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return 0.0f;
+}
+
+static inline BOOL CallDelegateReturningBoolean(BOOL result, WebView *self, id delegate, SEL selector)
+{
+ if (!delegate || ![delegate respondsToSelector:selector])
+ return result;
+ if (!self->_private->catchesDelegateExceptions)
+ return reinterpret_cast<BOOL (*)(id, SEL, WebView *)>(objc_msgSend)(delegate, selector, self);
+ @try {
+ return reinterpret_cast<BOOL (*)(id, SEL, WebView *)>(objc_msgSend)(delegate, selector, self);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return result;
+}
+
+static inline BOOL CallDelegateReturningBoolean(BOOL result, WebView *self, id delegate, SEL selector, id object)
+{
+ if (!delegate || ![delegate respondsToSelector:selector])
+ return result;
+ if (!self->_private->catchesDelegateExceptions)
+ return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id)>(objc_msgSend)(delegate, selector, self, object);
+ @try {
+ return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id)>(objc_msgSend)(delegate, selector, self, object);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return result;
+}
+
+static inline BOOL CallDelegateReturningBoolean(BOOL result, WebView *self, id delegate, SEL selector, id object, BOOL boolean)
+{
+ if (!delegate || ![delegate respondsToSelector:selector])
+ return result;
+ if (!self->_private->catchesDelegateExceptions)
+ return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id, BOOL)>(objc_msgSend)(delegate, selector, self, object, boolean);
+ @try {
+ return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id, BOOL)>(objc_msgSend)(delegate, selector, self, object, boolean);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return result;
+}
+
+static inline BOOL CallDelegateReturningBoolean(BOOL result, WebView *self, id delegate, SEL selector, id object1, id object2)
+{
+ if (!delegate || ![delegate respondsToSelector:selector])
+ return result;
+ if (!self->_private->catchesDelegateExceptions)
+ return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id, id)>(objc_msgSend)(delegate, selector, self, object1, object2);
+ @try {
+ return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id, id)>(objc_msgSend)(delegate, selector, self, object1, object2);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return result;
+}
+
+static inline id CallDelegate(IMP implementation, WebView *self, id delegate, SEL selector)
+{
+ if (!delegate)
+ return nil;
+ if (!self->_private->catchesDelegateExceptions)
+ return implementation(delegate, selector, self);
+ @try {
+ return implementation(delegate, selector, self);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return nil;
+}
+
+static inline id CallDelegate(IMP implementation, WebView *self, id delegate, SEL selector, id object)
+{
+ if (!delegate)
+ return nil;
+ if (!self->_private->catchesDelegateExceptions)
+ return implementation(delegate, selector, self, object);
+ @try {
+ return implementation(delegate, selector, self, object);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return nil;
+}
+
+static inline id CallDelegate(IMP implementation, WebView *self, id delegate, SEL selector, id object1, id object2)
+{
+ if (!delegate)
+ return nil;
+ if (!self->_private->catchesDelegateExceptions)
+ return implementation(delegate, selector, self, object1, object2);
+ @try {
+ return implementation(delegate, selector, self, object1, object2);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return nil;
+}
+
+static inline id CallDelegate(IMP implementation, WebView *self, id delegate, SEL selector, id object1, id object2, id object3)
+{
+ if (!delegate)
+ return nil;
+ if (!self->_private->catchesDelegateExceptions)
+ return implementation(delegate, selector, self, object1, object2, object3);
+ @try {
+ return implementation(delegate, selector, self, object1, object2, object3);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return nil;
+}
+
+static inline id CallDelegate(IMP implementation, WebView *self, id delegate, SEL selector, id object1, id object2, id object3, id object4)
+{
+ if (!delegate)
+ return nil;
+ if (!self->_private->catchesDelegateExceptions)
+ return implementation(delegate, selector, self, object1, object2, object3, object4);
+ @try {
+ return implementation(delegate, selector, self, object1, object2, object3, object4);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return nil;
+}
+
+static inline id CallDelegate(IMP implementation, WebView *self, id delegate, SEL selector, id object1, NSInteger integer, id object2)
+{
+ if (!delegate)
+ return nil;
+ if (!self->_private->catchesDelegateExceptions)
+ return implementation(delegate, selector, self, object1, integer, object2);
+ @try {
+ return implementation(delegate, selector, self, object1, integer, object2);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return nil;
+}
+
+static inline id CallDelegate(IMP implementation, WebView *self, id delegate, SEL selector, id object1, id object2, NSInteger integer, id object3)
+{
+ if (!delegate)
+ return nil;
+ if (!self->_private->catchesDelegateExceptions)
+ return implementation(delegate, selector, self, object1, object2, integer, object3);
+ @try {
+ return implementation(delegate, selector, self, object1, object2, integer, object3);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return nil;
+}
+
+static inline id CallDelegate(IMP implementation, WebView *self, id delegate, SEL selector, id object1, NSTimeInterval interval, id object2, id object3)
+{
+ if (!delegate)
+ return nil;
+ if (!self->_private->catchesDelegateExceptions)
+ return implementation(delegate, selector, self, object1, interval, object2, object3);
+ @try {
+ return implementation(delegate, selector, self, object1, interval, object2, object3);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return nil;
+}
+
+id CallUIDelegate(WebView *self, SEL selector)
+{
+ return CallDelegate(self, self->_private->UIDelegate, selector);
+}
+
+id CallUIDelegate(WebView *self, SEL selector, id object)
+{
+ return CallDelegate(self, self->_private->UIDelegate, selector, object);
+}
+
+id CallUIDelegate(WebView *self, SEL selector, id object, BOOL boolean)
+{
+ return CallDelegate(self, self->_private->UIDelegate, selector, object, boolean);
+}
+
+id CallUIDelegate(WebView *self, SEL selector, NSRect rect)
+{
+ return CallDelegate(self, self->_private->UIDelegate, selector, rect);
+}
+
+id CallUIDelegate(WebView *self, SEL selector, id object1, id object2)
+{
+ return CallDelegate(self, self->_private->UIDelegate, selector, object1, object2);
+}
+
+id CallUIDelegate(WebView *self, SEL selector, id object1, id object2, id object3)
+{
+ return CallDelegate(self, self->_private->UIDelegate, selector, object1, object2, object3);
+}
+
+id CallUIDelegate(WebView *self, SEL selector, id object, NSUInteger integer)
+{
+ return CallDelegate(self, self->_private->UIDelegate, selector, object, integer);
+}
+
+float CallUIDelegateReturningFloat(WebView *self, SEL selector)
+{
+ return CallDelegateReturningFloat(self, self->_private->UIDelegate, selector);
+}
+
+BOOL CallUIDelegateReturningBoolean(BOOL result, WebView *self, SEL selector)
+{
+ return CallDelegateReturningBoolean(result, self, self->_private->UIDelegate, selector);
+}
+
+BOOL CallUIDelegateReturningBoolean(BOOL result, WebView *self, SEL selector, id object)
+{
+ return CallDelegateReturningBoolean(result, self, self->_private->UIDelegate, selector, object);
+}
+
+BOOL CallUIDelegateReturningBoolean(BOOL result, WebView *self, SEL selector, id object, BOOL boolean)
+{
+ return CallDelegateReturningBoolean(result, self, self->_private->UIDelegate, selector, object, boolean);
+}
+
+BOOL CallUIDelegateReturningBoolean(BOOL result, WebView *self, SEL selector, id object1, id object2)
+{
+ return CallDelegateReturningBoolean(result, self, self->_private->UIDelegate, selector, object1, object2);
+}
+
+id CallFrameLoadDelegate(IMP implementation, WebView *self, SEL selector)
+{
+ return CallDelegate(implementation, self, self->_private->frameLoadDelegate, selector);
+}
+
+id CallFrameLoadDelegate(IMP implementation, WebView *self, SEL selector, id object)
+{
+ return CallDelegate(implementation, self, self->_private->frameLoadDelegate, selector, object);
+}
+
+id CallFrameLoadDelegate(IMP implementation, WebView *self, SEL selector, id object1, id object2)
+{
+ return CallDelegate(implementation, self, self->_private->frameLoadDelegate, selector, object1, object2);
+}
+
+id CallFrameLoadDelegate(IMP implementation, WebView *self, SEL selector, id object1, id object2, id object3)
+{
+ return CallDelegate(implementation, self, self->_private->frameLoadDelegate, selector, object1, object2, object3);
+}
+
+id CallFrameLoadDelegate(IMP implementation, WebView *self, SEL selector, id object1, id object2, id object3, id object4)
+{
+ return CallDelegate(implementation, self, self->_private->frameLoadDelegate, selector, object1, object2, object3, object4);
+}
+
+id CallFrameLoadDelegate(IMP implementation, WebView *self, SEL selector, id object1, NSTimeInterval interval, id object2, id object3)
+{
+ return CallDelegate(implementation, self, self->_private->frameLoadDelegate, selector, object1, interval, object2, object3);
+}
+
+id CallResourceLoadDelegate(IMP implementation, WebView *self, SEL selector, id object1, id object2)
+{
+ return CallDelegate(implementation, self, self->_private->resourceProgressDelegate, selector, object1, object2);
+}
+
+id CallResourceLoadDelegate(IMP implementation, WebView *self, SEL selector, id object1, id object2, id object3)
+{
+ return CallDelegate(implementation, self, self->_private->resourceProgressDelegate, selector, object1, object2, object3);
+}
+
+id CallResourceLoadDelegate(IMP implementation, WebView *self, SEL selector, id object1, id object2, id object3, id object4)
+{
+ return CallDelegate(implementation, self, self->_private->resourceProgressDelegate, selector, object1, object2, object3, object4);
+}
+
+id CallResourceLoadDelegate(IMP implementation, WebView *self, SEL selector, id object1, NSInteger integer, id object2)
+{
+ return CallDelegate(implementation, self, self->_private->resourceProgressDelegate, selector, object1, integer, object2);
+}
+
+id CallResourceLoadDelegate(IMP implementation, WebView *self, SEL selector, id object1, id object2, NSInteger integer, id object3)
+{
+ return CallDelegate(implementation, self, self->_private->resourceProgressDelegate, selector, object1, object2, integer, object3);
+}
+
+// The form delegate needs to have it's own implementation, because the first argument is never the WebView
+
+id CallFormDelegate(WebView *self, SEL selector, id object1, id object2)
+{
+ id delegate = self->_private->formDelegate;
+ if (!delegate || ![delegate respondsToSelector:selector])
+ return nil;
+ if (!self->_private->catchesDelegateExceptions)
+ return objc_msgSend(delegate, selector, object1, object2);
+ @try {
+ return objc_msgSend(delegate, selector, object1, object2);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return nil;
+}
+
+id CallFormDelegate(WebView *self, SEL selector, id object1, id object2, id object3, id object4, id object5)
+{
+ id delegate = self->_private->formDelegate;
+ if (!delegate || ![delegate respondsToSelector:selector])
+ return nil;
+ if (!self->_private->catchesDelegateExceptions)
+ return objc_msgSend(delegate, selector, object1, object2, object3, object4, object5);
+ @try {
+ return objc_msgSend(delegate, selector, object1, object2, object3, object4, object5);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return nil;
+}
+
+BOOL CallFormDelegateReturningBoolean(BOOL result, WebView *self, SEL selector, id object1, SEL selectorArg, id object2)
+{
+ id delegate = self->_private->formDelegate;
+ if (!delegate || ![delegate respondsToSelector:selector])
+ return result;
+ if (!self->_private->catchesDelegateExceptions)
+ return reinterpret_cast<BOOL (*)(id, SEL, id, SEL, id)>(objc_msgSend)(delegate, selector, object1, selectorArg, object2);
+ @try {
+ return reinterpret_cast<BOOL (*)(id, SEL, id, SEL, id)>(objc_msgSend)(delegate, selector, object1, selectorArg, object2);
+ } @catch(id exception) {
+ ReportDiscardedDelegateException(selector, exception);
+ }
+ return result;
+}
+
+@end