diff options
author | Ben Murdoch <benm@google.com> | 2011-05-13 16:23:25 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2011-05-16 11:35:02 +0100 |
commit | 65f03d4f644ce73618e5f4f50dd694b26f55ae12 (patch) | |
tree | f478babb801e720de7bfaee23443ffe029f58731 /Source/WebKit/mac/WebView | |
parent | 47de4a2fb7262c7ebdb9cd133ad2c54c187454d0 (diff) | |
download | external_webkit-65f03d4f644ce73618e5f4f50dd694b26f55ae12.zip external_webkit-65f03d4f644ce73618e5f4f50dd694b26f55ae12.tar.gz external_webkit-65f03d4f644ce73618e5f4f50dd694b26f55ae12.tar.bz2 |
Merge WebKit at r75993: Initial merge by git.
Change-Id: I602bbdc3974787a3b0450456a30a7868286921c3
Diffstat (limited to 'Source/WebKit/mac/WebView')
112 files changed, 35530 insertions, 0 deletions
diff --git a/Source/WebKit/mac/WebView/WebArchive.h b/Source/WebKit/mac/WebView/WebArchive.h new file mode 100644 index 0000000..cf54da5 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebArchive.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2004, 2005 Apple Computer, Inc. All rights reserved. + * + * 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 <Foundation/Foundation.h> + +@class WebArchivePrivate; +@class WebResource; + +/*! + @const WebArchivePboardType + @abstract The pasteboard type constant used when adding or accessing a WebArchive on the pasteboard. +*/ +extern NSString *WebArchivePboardType; + +/*! + @class WebArchive + @discussion WebArchive represents a main resource as well as all the subresources and subframes associated with the main resource. + The main resource can be an entire web page, a portion of a web page, or some other kind of data such as an image. + This class can be used for saving standalone web pages, representing portions of a web page on the pasteboard, or any other + application where one class is needed to represent rich web content. +*/ +@interface WebArchive : NSObject <NSCoding, NSCopying> +{ + @private + WebArchivePrivate *_private; +} + +/*! + @method initWithMainResource:subresources:subframeArchives: + @abstract The initializer for WebArchive. + @param mainResource The main resource of the archive. + @param subresources The subresources of the archive (can be nil). + @param subframeArchives The archives representing the subframes of the archive (can be nil). + @result An initialized WebArchive. +*/ +- (id)initWithMainResource:(WebResource *)mainResource subresources:(NSArray *)subresources subframeArchives:(NSArray *)subframeArchives; + +/*! + @method initWithData: + @abstract The initializer for creating a WebArchive from data. + @param data The data representing the archive. This can be obtained using WebArchive's data method. + @result An initialized WebArchive. +*/ +- (id)initWithData:(NSData *)data; + +/*! + @method mainResource + @result The main resource of the archive. +*/ +- (WebResource *)mainResource; + +/*! + @method subresources + @result The subresource of the archive (can be nil). +*/ +- (NSArray *)subresources; + +/*! + @method subframeArchives + @result The archives representing the subframes of the archive (can be nil). +*/ +- (NSArray *)subframeArchives; + +/*! + @method data + @result The data representation of the archive. + @discussion The data returned by this method can be used to save a web archive to a file or to place a web archive on the pasteboard + using WebArchivePboardType. To create a WebArchive using the returned data, call initWithData:. +*/ +- (NSData *)data; + +@end diff --git a/Source/WebKit/mac/WebView/WebArchive.mm b/Source/WebKit/mac/WebView/WebArchive.mm new file mode 100644 index 0000000..f07b51b --- /dev/null +++ b/Source/WebKit/mac/WebView/WebArchive.mm @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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 "WebArchive.h" +#import "WebArchiveInternal.h" + +#import "WebKitLogging.h" +#import "WebNSObjectExtras.h" +#import "WebResourceInternal.h" +#import "WebTypesInternal.h" +#import <JavaScriptCore/InitializeThreading.h> +#import <WebCore/ArchiveResource.h> +#import <WebCore/LegacyWebArchive.h> +#import <WebCore/ThreadCheck.h> +#import <WebCore/WebCoreObjCExtras.h> +#import <wtf/Threading.h> + +using namespace WebCore; + +NSString *WebArchivePboardType = @"Apple Web Archive pasteboard type"; + +static NSString * const WebMainResourceKey = @"WebMainResource"; +static NSString * const WebSubresourcesKey = @"WebSubresources"; +static NSString * const WebSubframeArchivesKey = @"WebSubframeArchives"; + +@interface WebArchivePrivate : NSObject { +@public + WebResource *cachedMainResource; + NSArray *cachedSubresources; + NSArray *cachedSubframeArchives; +@private + RefPtr<LegacyWebArchive> coreArchive; +} + +- (id)initWithCoreArchive:(PassRefPtr<LegacyWebArchive>)coreArchive; +- (LegacyWebArchive*)coreArchive; +- (void)setCoreArchive:(PassRefPtr<LegacyWebArchive>)newCoreArchive; +@end + +@implementation WebArchivePrivate + ++ (void)initialize +{ + JSC::initializeThreading(); + WTF::initializeMainThreadToProcessMainThread(); +#ifndef BUILDING_ON_TIGER + WebCoreObjCFinalizeOnMainThread(self); +#endif +} + +- (id)init +{ + self = [super init]; + if (!self) + return nil; + coreArchive = LegacyWebArchive::create(); + return self; +} + +- (id)initWithCoreArchive:(PassRefPtr<LegacyWebArchive>)_coreArchive +{ + self = [super init]; + if (!self || !_coreArchive) { + [self release]; + return nil; + } + coreArchive = _coreArchive; + return self; +} + +- (LegacyWebArchive*)coreArchive +{ + return coreArchive.get(); +} + +- (void)setCoreArchive:(PassRefPtr<LegacyWebArchive>)newCoreArchive +{ + ASSERT(coreArchive); + ASSERT(newCoreArchive); + coreArchive = newCoreArchive; +} + +- (void)dealloc +{ + if (WebCoreObjCScheduleDeallocateOnMainThread([WebArchivePrivate class], self)) + return; + + [cachedMainResource release]; + [cachedSubresources release]; + [cachedSubframeArchives release]; + + [super dealloc]; +} + +@end + +@implementation WebArchive + +- (id)init +{ + WebCoreThreadViolationCheckRoundTwo(); + + self = [super init]; + if (!self) + return nil; + _private = [[WebArchivePrivate alloc] init]; + return self; +} + +static BOOL isArrayOfClass(id object, Class elementClass) +{ + if (![object isKindOfClass:[NSArray class]]) + return NO; + NSArray *array = (NSArray *)object; + NSUInteger count = [array count]; + for (NSUInteger i = 0; i < count; ++i) + if (![[array objectAtIndex:i] isKindOfClass:elementClass]) + return NO; + return YES; +} + +- (id)initWithMainResource:(WebResource *)mainResource subresources:(NSArray *)subresources subframeArchives:(NSArray *)subframeArchives +{ +#ifdef MAIL_THREAD_WORKAROUND + if (needMailThreadWorkaround()) + return [[self _webkit_invokeOnMainThread] initWithMainResource:mainResource subresources:subresources subframeArchives:subframeArchives]; +#endif + + WebCoreThreadViolationCheckRoundTwo(); + + self = [super init]; + if (!self) + return nil; + + _private = [[WebArchivePrivate alloc] init]; + + _private->cachedMainResource = [mainResource retain]; + if (!_private->cachedMainResource) { + [self release]; + return nil; + } + + if (!subresources || isArrayOfClass(subresources, [WebResource class])) + _private->cachedSubresources = [subresources retain]; + else { + [self release]; + return nil; + } + + if (!subframeArchives || isArrayOfClass(subframeArchives, [WebArchive class])) + _private->cachedSubframeArchives = [subframeArchives retain]; + else { + [self release]; + return nil; + } + + RefPtr<ArchiveResource> coreMainResource = mainResource ? [mainResource _coreResource] : 0; + + Vector<PassRefPtr<ArchiveResource> > coreResources; + NSEnumerator *enumerator = [subresources objectEnumerator]; + WebResource *subresource; + while ((subresource = [enumerator nextObject]) != nil) + coreResources.append([subresource _coreResource]); + + Vector<PassRefPtr<LegacyWebArchive> > coreArchives; + enumerator = [subframeArchives objectEnumerator]; + WebArchive *subframeArchive; + while ((subframeArchive = [enumerator nextObject]) != nil) + coreArchives.append([subframeArchive->_private coreArchive]); + + [_private setCoreArchive:LegacyWebArchive::create(coreMainResource.release(), coreResources, coreArchives)]; + if (![_private coreArchive]) { + [self release]; + return nil; + } + + return self; +} + +- (id)initWithData:(NSData *)data +{ + WebCoreThreadViolationCheckRoundTwo(); + + self = [super init]; + if (!self) + return nil; + +#if !LOG_DISABLED + CFAbsoluteTime start = CFAbsoluteTimeGetCurrent(); +#endif + + _private = [[WebArchivePrivate alloc] init]; + RefPtr<LegacyWebArchive> coreArchive = LegacyWebArchive::create(SharedBuffer::wrapNSData(data).get()); + if (!coreArchive) { + [self release]; + return nil; + } + + [_private setCoreArchive:coreArchive.release()]; + +#if !LOG_DISABLED + CFAbsoluteTime end = CFAbsoluteTimeGetCurrent(); + CFAbsoluteTime duration = end - start; +#endif + LOG(Timing, "Parsing web archive with [NSPropertyListSerialization propertyListFromData::::] took %f seconds", duration); + + return self; +} + +- (id)initWithCoder:(NSCoder *)decoder +{ + WebResource *mainResource = nil; + NSArray *subresources = nil; + NSArray *subframeArchives = nil; + + @try { + id object = [decoder decodeObjectForKey:WebMainResourceKey]; + if ([object isKindOfClass:[WebResource class]]) + mainResource = object; + object = [decoder decodeObjectForKey:WebSubresourcesKey]; + if (isArrayOfClass(object, [WebResource class])) + subresources = object; + object = [decoder decodeObjectForKey:WebSubframeArchivesKey]; + if (isArrayOfClass(object, [WebArchive class])) + subframeArchives = object; + } @catch(id) { + [self release]; + return nil; + } + + return [self initWithMainResource:mainResource subresources:subresources subframeArchives:subframeArchives]; +} + +- (void)encodeWithCoder:(NSCoder *)encoder +{ + [encoder encodeObject:[self mainResource] forKey:WebMainResourceKey]; + [encoder encodeObject:[self subresources] forKey:WebSubresourcesKey]; + [encoder encodeObject:[self subframeArchives] forKey:WebSubframeArchivesKey]; +} + +- (void)dealloc +{ + [_private release]; + [super dealloc]; +} + +- (id)copyWithZone:(NSZone *)zone +{ + return [self retain]; +} + +- (WebResource *)mainResource +{ +#ifdef MAIL_THREAD_WORKAROUND + if (needMailThreadWorkaround()) + return [[self _webkit_invokeOnMainThread] mainResource]; +#endif + + WebCoreThreadViolationCheckRoundTwo(); + + // Currently from WebKit API perspective, WebArchives are entirely immutable once created + // If they ever become mutable, we'll need to rethink this. + if (!_private->cachedMainResource) { + LegacyWebArchive* coreArchive = [_private coreArchive]; + if (coreArchive) + _private->cachedMainResource = [[WebResource alloc] _initWithCoreResource:coreArchive->mainResource()]; + } + + return [[_private->cachedMainResource retain] autorelease]; +} + +- (NSArray *)subresources +{ +#ifdef MAIL_THREAD_WORKAROUND + if (needMailThreadWorkaround()) + return [[self _webkit_invokeOnMainThread] subresources]; +#endif + + WebCoreThreadViolationCheckRoundTwo(); + + // Currently from WebKit API perspective, WebArchives are entirely immutable once created + // If they ever become mutable, we'll need to rethink this. + if (!_private->cachedSubresources) { + LegacyWebArchive* coreArchive = [_private coreArchive]; + if (!coreArchive) + _private->cachedSubresources = [[NSArray alloc] init]; + else { + const Vector<RefPtr<ArchiveResource> >& subresources(coreArchive->subresources()); + NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithCapacity:subresources.size()]; + _private->cachedSubresources = mutableArray; + for (unsigned i = 0; i < subresources.size(); ++i) { + WebResource *resource = [[WebResource alloc] _initWithCoreResource:subresources[i].get()]; + if (resource) { + [mutableArray addObject:resource]; + [resource release]; + } + } + } + } + // Maintain the WebKit 3 behavior of this API, which is documented and + // relied upon by some clients, of returning nil if there are no subresources. + return [_private->cachedSubresources count] ? [[_private->cachedSubresources retain] autorelease] : nil; +} + +- (NSArray *)subframeArchives +{ +#ifdef MAIL_THREAD_WORKAROUND + if (needMailThreadWorkaround()) + return [[self _webkit_invokeOnMainThread] subframeArchives]; +#endif + + WebCoreThreadViolationCheckRoundTwo(); + + // Currently from WebKit API perspective, WebArchives are entirely immutable once created + // If they ever become mutable, we'll need to rethink this. + if (!_private->cachedSubframeArchives) { + LegacyWebArchive* coreArchive = [_private coreArchive]; + if (!coreArchive) + _private->cachedSubframeArchives = [[NSArray alloc] init]; + else { + const Vector<RefPtr<Archive> >& subframeArchives(coreArchive->subframeArchives()); + NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithCapacity:subframeArchives.size()]; + _private->cachedSubframeArchives = mutableArray; + for (unsigned i = 0; i < subframeArchives.size(); ++i) { + WebArchive *archive = [[WebArchive alloc] _initWithCoreLegacyWebArchive:(LegacyWebArchive *)subframeArchives[i].get()]; + [mutableArray addObject:archive]; + [archive release]; + } + } + } + + return [[_private->cachedSubframeArchives retain] autorelease]; +} + +- (NSData *)data +{ + WebCoreThreadViolationCheckRoundTwo(); + +#if !LOG_DISABLED + CFAbsoluteTime start = CFAbsoluteTimeGetCurrent(); +#endif + + RetainPtr<CFDataRef> data = [_private coreArchive]->rawDataRepresentation(); + +#if !LOG_DISABLED + CFAbsoluteTime end = CFAbsoluteTimeGetCurrent(); + CFAbsoluteTime duration = end - start; +#endif + LOG(Timing, "Serializing web archive to raw CFPropertyList data took %f seconds", duration); + + return [[(NSData *)data.get() retain] autorelease]; +} + +@end + +@implementation WebArchive (WebInternal) + +- (id)_initWithCoreLegacyWebArchive:(PassRefPtr<WebCore::LegacyWebArchive>)coreLegacyWebArchive +{ + WebCoreThreadViolationCheckRoundTwo(); + + self = [super init]; + if (!self) + return nil; + + _private = [[WebArchivePrivate alloc] initWithCoreArchive:coreLegacyWebArchive]; + if (!_private) { + [self release]; + return nil; + } + + return self; +} + +- (WebCore::LegacyWebArchive *)_coreLegacyWebArchive +{ + WebCoreThreadViolationCheckRoundTwo(); + + return [_private coreArchive]; +} + +@end diff --git a/Source/WebKit/mac/WebView/WebArchiveInternal.h b/Source/WebKit/mac/WebView/WebArchiveInternal.h new file mode 100644 index 0000000..071d4ab --- /dev/null +++ b/Source/WebKit/mac/WebView/WebArchiveInternal.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * 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 "WebArchive.h" +#import <JavaScriptCore/Forward.h> + +namespace WebCore { + class LegacyWebArchive; +} + +@interface WebArchive (WebInternal) + +- (id)_initWithCoreLegacyWebArchive:(WTF::PassRefPtr<WebCore::LegacyWebArchive>)coreLegacyWebArchive; +- (WebCore::LegacyWebArchive *)_coreLegacyWebArchive; + +@end diff --git a/Source/WebKit/mac/WebView/WebClipView.h b/Source/WebKit/mac/WebView/WebClipView.h new file mode 100644 index 0000000..968d2de --- /dev/null +++ b/Source/WebKit/mac/WebView/WebClipView.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * 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 <AppKit/AppKit.h> + +@interface WebClipView : NSClipView +{ + BOOL _haveAdditionalClip; + BOOL _isScrolling; + NSRect _additionalClip; +} + +- (void)setAdditionalClip:(NSRect)additionalClip; +- (void)resetAdditionalClip; +- (BOOL)hasAdditionalClip; +- (NSRect)additionalClip; + +@end diff --git a/Source/WebKit/mac/WebView/WebClipView.mm b/Source/WebKit/mac/WebView/WebClipView.mm new file mode 100644 index 0000000..ced5d8d --- /dev/null +++ b/Source/WebKit/mac/WebView/WebClipView.mm @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * 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 "WebClipView.h" + +#import "WebFrameInternal.h" +#import "WebFrameView.h" +#import "WebViewPrivate.h" +#import <WebCore/FrameView.h> + +// WebClipView's entire reason for existing is to set the clip used by focus ring redrawing. +// There's no easy way to prevent the focus ring from drawing outside the passed-in clip rectangle +// because it expects to have to draw outside the bounds of the view it's being drawn for. +// But it looks for the enclosing clip view, which gives us a hook we can use to control it. +// The "additional clip" is a clip for focus ring redrawing. + +// FIXME: Change terminology from "additional clip" to "focus ring clip". + +using namespace WebCore; + +@interface NSView (WebViewMethod) +- (WebView *)_webView; +@end + +@interface NSClipView (WebNSClipViewDetails) +- (void)_immediateScrollToPoint:(NSPoint)newOrigin; +@end + +@implementation WebClipView + +- (id)initWithFrame:(NSRect)frame +{ + self = [super initWithFrame:frame]; + if (!self) + return nil; + + // In WebHTMLView, we set a clip. This is not typical to do in an + // NSView, and while correct for any one invocation of drawRect:, + // it causes some bad problems if that clip is cached between calls. + // The cached graphics state, which clip views keep around, does + // cache the clip in this undesirable way. Consequently, we want to + // release the GState for all clip views for all views contained in + // a WebHTMLView. Here we do it for subframes, which use WebClipView. + // See these bugs for more information: + // <rdar://problem/3409315>: REGRESSSION (7B58-7B60)?: Safari draws blank frames on macosx.apple.com perf page + [self releaseGState]; + + return self; +} + +#if USE(ACCELERATED_COMPOSITING) +- (NSRect)visibleRect +{ + if (!_isScrolling) + return [super visibleRect]; + + WebFrameView *webFrameView = (WebFrameView *)[[self superview] superview]; + if (![webFrameView isKindOfClass:[WebFrameView class]]) + return [super visibleRect]; + + if (Frame* coreFrame = core([webFrameView webFrame])) { + if (FrameView* frameView = coreFrame->view()) { + if (frameView->isEnclosedInCompositingLayer()) + return [self bounds]; + } + } + + return [super visibleRect]; +} + +- (void)_immediateScrollToPoint:(NSPoint)newOrigin +{ + _isScrolling = YES; + [super _immediateScrollToPoint:newOrigin]; + _isScrolling = NO; +} +#endif + +- (void)resetAdditionalClip +{ + ASSERT(_haveAdditionalClip); + _haveAdditionalClip = NO; +} + +- (void)setAdditionalClip:(NSRect)additionalClip +{ + ASSERT(!_haveAdditionalClip); + _haveAdditionalClip = YES; + _additionalClip = additionalClip; +} + +- (BOOL)hasAdditionalClip +{ + return _haveAdditionalClip; +} + +- (NSRect)additionalClip +{ + ASSERT(_haveAdditionalClip); + return _additionalClip; +} + +- (NSRect)_focusRingVisibleRect +{ + NSRect rect = [self visibleRect]; + if (_haveAdditionalClip) { + rect = NSIntersectionRect(rect, _additionalClip); + } + return rect; +} + +- (void)scrollWheel:(NSEvent *)event +{ + NSView *docView = [self documentView]; + if ([docView respondsToSelector:@selector(_webView)]) { +#if ENABLE(DASHBOARD_SUPPORT) + WebView *wv = [docView _webView]; + if ([wv _dashboardBehavior:WebDashboardBehaviorAllowWheelScrolling]) { + [super scrollWheel:event]; + } +#endif + return; + } + [super scrollWheel:event]; +} + +@end diff --git a/Source/WebKit/mac/WebView/WebDashboardRegion.h b/Source/WebKit/mac/WebView/WebDashboardRegion.h new file mode 100644 index 0000000..4963d04 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDashboardRegion.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2004 Apple Computer, Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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. + */ + +#if !defined(ENABLE_DASHBOARD_SUPPORT) +#define ENABLE_DASHBOARD_SUPPORT 1 +#endif + +#if ENABLE_DASHBOARD_SUPPORT + +typedef enum { + WebDashboardRegionTypeNone, + WebDashboardRegionTypeCircle, + WebDashboardRegionTypeRectangle, + WebDashboardRegionTypeScrollerRectangle +} WebDashboardRegionType; + +@interface WebDashboardRegion : NSObject <NSCopying> +{ + NSRect rect; + NSRect clip; + WebDashboardRegionType type; +} +- initWithRect:(NSRect)rect clip:(NSRect)clip type:(WebDashboardRegionType)type; +- (NSRect)dashboardRegionClip; +- (NSRect)dashboardRegionRect; +- (WebDashboardRegionType)dashboardRegionType; +@end + +#endif diff --git a/Source/WebKit/mac/WebView/WebDashboardRegion.mm b/Source/WebKit/mac/WebView/WebDashboardRegion.mm new file mode 100644 index 0000000..c44977a --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDashboardRegion.mm @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2004 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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 "WebDashboardRegion.h" + +#import <wtf/UnusedParam.h> + +#if ENABLE(DASHBOARD_SUPPORT) + +@implementation WebDashboardRegion + +- initWithRect:(NSRect)r clip:(NSRect)c type:(WebDashboardRegionType)t +{ + self = [super init]; + rect = r; + clip = c; + type = t; + return self; +} + +- (id)copyWithZone:(NSZone *)unusedZone +{ + UNUSED_PARAM(unusedZone); + + return [self retain]; +} + +- (NSRect)dashboardRegionClip +{ + return clip; +} + +- (NSRect)dashboardRegionRect +{ + return rect; +} + +- (WebDashboardRegionType)dashboardRegionType +{ + return type; +} + +static const char* typeName(WebDashboardRegionType type) +{ + switch (type) { + case WebDashboardRegionTypeNone: + return "None"; + case WebDashboardRegionTypeCircle: + return "Circle"; + case WebDashboardRegionTypeRectangle: + return "Rectangle"; + case WebDashboardRegionTypeScrollerRectangle: + return "ScrollerRectangle"; + } + return "Unknown"; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"rect:%@ clip:%@ type:%s", NSStringFromRect(rect), NSStringFromRect(clip), typeName(type)]; +} + +// FIXME: Overriding isEqual: without overriding hash will cause trouble if this ever goes into a NSSet or is the key in an NSDictionary. +- (BOOL)isEqual:(id)other +{ + return NSEqualRects(rect, [other dashboardRegionRect]) && NSEqualRects(clip, [other dashboardRegionClip]) && type == [other dashboardRegionType]; +} + +@end + +#endif diff --git a/Source/WebKit/mac/WebView/WebDataSource.h b/Source/WebKit/mac/WebView/WebDataSource.h new file mode 100644 index 0000000..1ffd339 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDataSource.h @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2003, 2004, 2005 Apple Computer, Inc. All rights reserved. + * + * 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 <Cocoa/Cocoa.h> + +#import <WebKit/WebDocument.h> + +@class NSMutableURLRequest; +@class NSURLConnection; +@class NSURLRequest; +@class NSURLResponse; +@class WebArchive; +@class WebDataSourcePrivate; +@class WebFrame; +@class WebResource; + +/*! + @class WebDataSource + @discussion A WebDataSource represents the data associated with a web page. + A datasource has a WebDocumentRepresentation which holds an appropriate + representation of the data. WebDataSources manage a hierarchy of WebFrames. + WebDataSources are typically related to a view by their containing WebFrame. +*/ +@interface WebDataSource : NSObject +{ +@private + WebDataSourcePrivate *_private; +} + +/*! + @method initWithRequest: + @abstract The designated initializer for WebDataSource. + @param request The request to use in creating a datasource. + @result Returns an initialized WebDataSource. +*/ +- (id)initWithRequest:(NSURLRequest *)request; + +/*! + @method data + @discussion The data will be incomplete until the datasource has completely loaded. + @result Returns the raw data associated with the datasource. Returns nil + if the datasource hasn't loaded any data. +*/ +- (NSData *)data; + +/*! + @method representation + @discussion A representation holds a type specific representation + of the datasource's data. The representation class is determined by mapping + a MIME type to a class. The representation is created once the MIME type + of the datasource content has been determined. + @result Returns the representation associated with this datasource. + Returns nil if the datasource hasn't created it's representation. +*/ +- (id <WebDocumentRepresentation>)representation; + +/*! + @method webFrame + @result Return the frame that represents this data source. +*/ +- (WebFrame *)webFrame; + +/*! + @method initialRequest + @result Returns a reference to the original request that created the + datasource. This request will be unmodified by WebKit. +*/ +- (NSURLRequest *)initialRequest; + +/*! + @method request + @result Returns the request that was used to create this datasource. +*/ +- (NSMutableURLRequest *)request; + +/*! + @method response + @result returns the WebResourceResponse for the data source. +*/ +- (NSURLResponse *)response; + +/*! + @method textEncodingName + @result Returns either the override encoding, as set on the WebView for this + dataSource or the encoding from the response. +*/ +- (NSString *)textEncodingName; + +/*! + @method isLoading + @discussion Returns YES if there are any pending loads. +*/ +- (BOOL)isLoading; + +/*! + @method pageTitle + @result Returns nil or the page title. +*/ +- (NSString *)pageTitle; + +/*! + @method unreachableURL + @discussion This will be non-nil only for dataSources created by calls to the + WebFrame method loadAlternateHTMLString:baseURL:forUnreachableURL:. + @result returns the unreachableURL for which this dataSource is showing alternate content, or nil +*/ +- (NSURL *)unreachableURL; + +/*! + @method webArchive + @result A WebArchive representing the data source, its subresources and child frames. + @description This method constructs a WebArchive using the original downloaded data. + In the case of HTML, if the current state of the document is preferred, webArchive should be + called on the DOM document instead. +*/ +- (WebArchive *)webArchive; + +/*! + @method mainResource + @result A WebResource representing the data source. + @description This method constructs a WebResource using the original downloaded data. + This method can be used to construct a WebArchive in case the archive returned by + WebDataSource's webArchive isn't sufficient. +*/ +- (WebResource *)mainResource; + +/*! + @method subresources + @abstract Returns all the subresources associated with the data source. + @description The returned array only contains subresources that have fully downloaded. +*/ +- (NSArray *)subresources; + +/*! + method subresourceForURL: + @abstract Returns a subresource for a given URL. + @param URL The URL of the subresource. + @description Returns non-nil if the data source has fully downloaded a subresource with the given URL. +*/ +- (WebResource *)subresourceForURL:(NSURL *)URL; + +/*! + @method addSubresource: + @abstract Adds a subresource to the data source. + @param subresource The subresource to be added. + @description addSubresource: adds a subresource to the data source's list of subresources. + Later, if something causes the data source to load the URL of the subresource, the data source + will load the data from the subresource instead of from the network. For example, if one wants to add + an image that is already downloaded to a web page, addSubresource: can be called so that the data source + uses the downloaded image rather than accessing the network. NOTE: If the data source already has a + subresource with the same URL, addSubresource: will replace it. +*/ +- (void)addSubresource:(WebResource *)subresource; + +@end diff --git a/Source/WebKit/mac/WebView/WebDataSource.mm b/Source/WebKit/mac/WebView/WebDataSource.mm new file mode 100644 index 0000000..02dbc2c --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDataSource.mm @@ -0,0 +1,534 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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 "WebDataSource.h" + +#import "WebArchive.h" +#import "WebArchiveInternal.h" +#import "WebDataSourceInternal.h" +#import "WebDocument.h" +#import "WebDocumentLoaderMac.h" +#import "WebFrameInternal.h" +#import "WebFrameLoadDelegate.h" +#import "WebFrameLoaderClient.h" +#import "WebHTMLRepresentation.h" +#import "WebKitErrorsPrivate.h" +#import "WebKitLogging.h" +#import "WebKitStatisticsPrivate.h" +#import "WebKitNSStringExtras.h" +#import "WebNSURLExtras.h" +#import "WebNSURLRequestExtras.h" +#import "WebPDFRepresentation.h" +#import "WebResourceInternal.h" +#import "WebResourceLoadDelegate.h" +#import "WebViewInternal.h" +#import <WebCore/ApplicationCacheStorage.h> +#import <WebCore/FrameLoader.h> +#import <WebCore/KURL.h> +#import <WebCore/LegacyWebArchive.h> +#import <WebCore/MIMETypeRegistry.h> +#import <WebCore/ResourceRequest.h> +#import <WebCore/SharedBuffer.h> +#import <WebCore/WebCoreObjCExtras.h> +#import <WebCore/WebCoreURLResponse.h> +#import <WebKit/DOMHTML.h> +#import <WebKit/DOMPrivate.h> +#import <runtime/InitializeThreading.h> +#import <wtf/Assertions.h> +#import <wtf/Threading.h> + +using namespace WebCore; + +@interface WebDataSourcePrivate : NSObject { +@public + WebDocumentLoaderMac* loader; + + id <WebDocumentRepresentation> representation; + + BOOL representationFinishedLoading; + BOOL includedInWebKitStatistics; +} +@end + +@implementation WebDataSourcePrivate + ++ (void)initialize +{ + JSC::initializeThreading(); + WTF::initializeMainThreadToProcessMainThread(); +#ifndef BUILDING_ON_TIGER + WebCoreObjCFinalizeOnMainThread(self); +#endif +} + +- (void)dealloc +{ + if (WebCoreObjCScheduleDeallocateOnMainThread([WebDataSourcePrivate class], self)) + return; + + ASSERT(loader); + if (loader) { + ASSERT(!loader->isLoading()); + loader->detachDataSource(); + loader->deref(); + } + + [representation release]; + + [super dealloc]; +} + +- (void)finalize +{ + ASSERT_MAIN_THREAD(); + + ASSERT(loader); + if (loader) { + ASSERT(!loader->isLoading()); + loader->detachDataSource(); + loader->deref(); + } + + [super finalize]; +} + +@end + +@interface WebDataSource (WebFileInternal) +@end + +@implementation WebDataSource (WebFileInternal) + +- (void)_setRepresentation:(id<WebDocumentRepresentation>)representation +{ + [_private->representation release]; + _private->representation = [representation retain]; + _private->representationFinishedLoading = NO; +} + +static inline void addTypesFromClass(NSMutableDictionary *allTypes, Class objCClass, NSArray *supportTypes) +{ + NSEnumerator *enumerator = [supportTypes objectEnumerator]; + ASSERT(enumerator != nil); + NSString *mime = nil; + while ((mime = [enumerator nextObject]) != nil) { + // Don't clobber previously-registered classes. + if ([allTypes objectForKey:mime] == nil) + [allTypes setObject:objCClass forKey:mime]; + } +} + ++ (Class)_representationClassForMIMEType:(NSString *)MIMEType allowingPlugins:(BOOL)allowPlugins +{ + Class repClass; + return [WebView _viewClass:nil andRepresentationClass:&repClass forMIMEType:MIMEType allowingPlugins:allowPlugins] ? repClass : nil; +} +@end + +@implementation WebDataSource (WebPrivate) + +- (NSError *)_mainDocumentError +{ + return _private->loader->mainDocumentError(); +} + +- (void)_addSubframeArchives:(NSArray *)subframeArchives +{ + // FIXME: This SPI is poor, poor design. Can we come up with another solution for those who need it? + DocumentLoader* loader = [self _documentLoader]; + ASSERT(loader); + + NSEnumerator *enumerator = [subframeArchives objectEnumerator]; + WebArchive *archive; + while ((archive = [enumerator nextObject]) != nil) + loader->addAllArchiveResources([archive _coreLegacyWebArchive]); +} + +- (NSFileWrapper *)_fileWrapperForURL:(NSURL *)URL +{ + if ([URL isFileURL]) { + NSString *path = [[URL path] stringByResolvingSymlinksInPath]; + return [[[NSFileWrapper alloc] initWithPath:path] autorelease]; + } + + WebResource *resource = [self subresourceForURL:URL]; + if (resource) + return [resource _fileWrapperRepresentation]; + + NSCachedURLResponse *cachedResponse = [[self _webView] _cachedResponseForURL:URL]; + if (cachedResponse) { + NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:[cachedResponse data]] autorelease]; + [wrapper setPreferredFilename:[[cachedResponse response] suggestedFilename]]; + return wrapper; + } + + return nil; +} + +- (NSString *)_responseMIMEType +{ + return [[self response] MIMEType]; +} + +- (BOOL)_transferApplicationCache:(NSString*)destinationBundleIdentifier +{ +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + DocumentLoader* loader = [self _documentLoader]; + + if (!loader) + return NO; + + NSString *cacheDir = [NSString _webkit_localCacheDirectoryWithBundleIdentifier:destinationBundleIdentifier]; + + return ApplicationCacheStorage::storeCopyOfCache(cacheDir, loader->applicationCacheHost()); +#else + return NO; +#endif +} + +- (void)_setDeferMainResourceDataLoad:(BOOL)flag +{ + DocumentLoader* loader = [self _documentLoader]; + + if (!loader) + return; + + loader->setDeferMainResourceDataLoad(flag); +} + +@end + +@implementation WebDataSource (WebInternal) + +- (void)_finishedLoading +{ + _private->representationFinishedLoading = YES; + [[self representation] finishedLoadingWithDataSource:self]; +} + +- (void)_receivedData:(NSData *)data +{ + // protect self temporarily, as the bridge receivedData call could remove our last ref + RetainPtr<WebDataSource*> protect(self); + + [[self representation] receivedData:data withDataSource:self]; + + if ([[self _webView] _usesDocumentViews]) + [[[[self webFrame] frameView] documentView] dataSourceUpdated:self]; +} + +- (void)_setMainDocumentError:(NSError *)error +{ + if (!_private->representationFinishedLoading) { + _private->representationFinishedLoading = YES; + [[self representation] receivedError:error withDataSource:self]; + } +} + +- (void)_revertToProvisionalState +{ + [self _setRepresentation:nil]; +} + ++ (NSMutableDictionary *)_repTypesAllowImageTypeOmission:(BOOL)allowImageTypeOmission +{ + static NSMutableDictionary *repTypes = nil; + static BOOL addedImageTypes = NO; + + if (!repTypes) { + repTypes = [[NSMutableDictionary alloc] init]; + addTypesFromClass(repTypes, [WebHTMLRepresentation class], [WebHTMLRepresentation supportedNonImageMIMETypes]); + + // Since this is a "secret default" we don't both registering it. + BOOL omitPDFSupport = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitOmitPDFSupport"]; + if (!omitPDFSupport) + addTypesFromClass(repTypes, [WebPDFRepresentation class], [WebPDFRepresentation supportedMIMETypes]); + } + + if (!addedImageTypes && !allowImageTypeOmission) { + addTypesFromClass(repTypes, [WebHTMLRepresentation class], [WebHTMLRepresentation supportedImageMIMETypes]); + addedImageTypes = YES; + } + + return repTypes; +} + +- (void)_replaceSelectionWithArchive:(WebArchive *)archive selectReplacement:(BOOL)selectReplacement +{ + DOMDocumentFragment *fragment = [self _documentFragmentWithArchive:archive]; + if (fragment) + [[self webFrame] _replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:NO matchStyle:NO]; +} + +// FIXME: There are few reasons why this method and many of its related methods can't be pushed entirely into WebCore in the future. +- (DOMDocumentFragment *)_documentFragmentWithArchive:(WebArchive *)archive +{ + ASSERT(archive); + WebResource *mainResource = [archive mainResource]; + if (mainResource) { + NSString *MIMEType = [mainResource MIMEType]; + if ([WebView canShowMIMETypeAsHTML:MIMEType]) { + NSString *markupString = [[NSString alloc] initWithData:[mainResource data] encoding:NSUTF8StringEncoding]; + // FIXME: seems poor form to do this as a side effect of getting a document fragment + if (DocumentLoader* loader = [self _documentLoader]) + loader->addAllArchiveResources([archive _coreLegacyWebArchive]); + + DOMDocumentFragment *fragment = [[self webFrame] _documentFragmentWithMarkupString:markupString baseURLString:[[mainResource URL] _web_originalDataAsString]]; + [markupString release]; + return fragment; + } else if (MIMETypeRegistry::isSupportedImageMIMEType(MIMEType)) { + return [self _documentFragmentWithImageResource:mainResource]; + + } + } + return nil; +} + +- (DOMDocumentFragment *)_documentFragmentWithImageResource:(WebResource *)resource +{ + DOMElement *imageElement = [self _imageElementWithImageResource:resource]; + if (!imageElement) + return 0; + DOMDocumentFragment *fragment = [[[self webFrame] DOMDocument] createDocumentFragment]; + [fragment appendChild:imageElement]; + return fragment; +} + +- (DOMElement *)_imageElementWithImageResource:(WebResource *)resource +{ + if (!resource) + return 0; + + [self addSubresource:resource]; + + DOMElement *imageElement = [[[self webFrame] DOMDocument] createElement:@"img"]; + + // FIXME: calling _web_originalDataAsString on a file URL returns an absolute path. Workaround this. + NSURL *URL = [resource URL]; + [imageElement setAttribute:@"src" value:[URL isFileURL] ? [URL absoluteString] : [URL _web_originalDataAsString]]; + + return imageElement; +} + +// May return nil if not initialized with a URL. +- (NSURL *)_URL +{ + const KURL& url = _private->loader->url(); + if (url.isEmpty()) + return nil; + return url; +} + +- (WebView *)_webView +{ + return [[self webFrame] webView]; +} + +- (BOOL)_isDocumentHTML +{ + NSString *MIMEType = [self _responseMIMEType]; + return [WebView canShowMIMETypeAsHTML:MIMEType]; +} + +- (void)_makeRepresentation +{ + Class repClass = [[self class] _representationClassForMIMEType:[self _responseMIMEType] allowingPlugins:[[[self _webView] preferences] arePlugInsEnabled]]; + + // Check if the data source was already bound? + if (![[self representation] isKindOfClass:repClass]) { + id newRep = repClass != nil ? [[repClass alloc] init] : nil; + [self _setRepresentation:(id <WebDocumentRepresentation>)newRep]; + [newRep release]; + } + + [_private->representation setDataSource:self]; +} + +- (DocumentLoader*)_documentLoader +{ + return _private->loader; +} + +- (id)_initWithDocumentLoader:(PassRefPtr<WebDocumentLoaderMac>)loader +{ + self = [super init]; + if (!self) + return nil; + + _private = [[WebDataSourcePrivate alloc] init]; + + _private->loader = loader.releaseRef(); + + LOG(Loading, "creating datasource for %@", static_cast<NSURL *>(_private->loader->request().url())); + + if ((_private->includedInWebKitStatistics = [[self webFrame] _isIncludedInWebKitStatistics])) + ++WebDataSourceCount; + + return self; +} + +@end + +@implementation WebDataSource + +- (id)initWithRequest:(NSURLRequest *)request +{ + return [self _initWithDocumentLoader:WebDocumentLoaderMac::create(request, SubstituteData())]; +} + +- (void)dealloc +{ + if (_private && _private->includedInWebKitStatistics) + --WebDataSourceCount; + + [_private release]; + + [super dealloc]; +} + +- (void)finalize +{ + if (_private && _private->includedInWebKitStatistics) + --WebDataSourceCount; + + [super finalize]; +} + +- (NSData *)data +{ + RefPtr<SharedBuffer> mainResourceData = _private->loader->mainResourceData(); + if (!mainResourceData) + return nil; + return [mainResourceData->createNSData() autorelease]; +} + +- (id <WebDocumentRepresentation>)representation +{ + return _private->representation; +} + +- (WebFrame *)webFrame +{ + FrameLoader* frameLoader = _private->loader->frameLoader(); + if (!frameLoader) + return nil; + return static_cast<WebFrameLoaderClient*>(frameLoader->client())->webFrame(); +} + +- (NSURLRequest *)initialRequest +{ + return _private->loader->originalRequest().nsURLRequest(); +} + +- (NSMutableURLRequest *)request +{ + FrameLoader* frameLoader = _private->loader->frameLoader(); + if (!frameLoader || !frameLoader->frameHasLoaded()) + return nil; + + // FIXME: this cast is dubious + return (NSMutableURLRequest *)_private->loader->request().nsURLRequest(); +} + +- (NSURLResponse *)response +{ + return _private->loader->response().nsURLResponse(); +} + +- (NSString *)textEncodingName +{ + NSString *textEncodingName = _private->loader->overrideEncoding(); + if (!textEncodingName) + textEncodingName = [[self response] textEncodingName]; + return textEncodingName; +} + +- (BOOL)isLoading +{ + return _private->loader->isLoadingInAPISense(); +} + +// Returns nil or the page title. +- (NSString *)pageTitle +{ + return [[self representation] title]; +} + +- (NSURL *)unreachableURL +{ + const KURL& unreachableURL = _private->loader->unreachableURL(); + if (unreachableURL.isEmpty()) + return nil; + return unreachableURL; +} + +- (WebArchive *)webArchive +{ + // it makes no sense to grab a WebArchive from an uncommitted document. + if (!_private->loader->isCommitted()) + return nil; + + return [[[WebArchive alloc] _initWithCoreLegacyWebArchive:LegacyWebArchive::create(core([self webFrame]))] autorelease]; +} + +- (WebResource *)mainResource +{ + RefPtr<ArchiveResource> coreResource = _private->loader->mainResource(); + return [[[WebResource alloc] _initWithCoreResource:coreResource.release()] autorelease]; +} + +- (NSArray *)subresources +{ + Vector<PassRefPtr<ArchiveResource> > coreSubresources; + _private->loader->getSubresources(coreSubresources); + + NSMutableArray *subresources = [[NSMutableArray alloc] initWithCapacity:coreSubresources.size()]; + for (unsigned i = 0; i < coreSubresources.size(); ++i) { + WebResource *resource = [[WebResource alloc] _initWithCoreResource:coreSubresources[i]]; + if (resource) { + [subresources addObject:resource]; + [resource release]; + } + } + + return [subresources autorelease]; +} + +- (WebResource *)subresourceForURL:(NSURL *)URL +{ + RefPtr<ArchiveResource> subresource = _private->loader->subresource(URL); + + return subresource ? [[[WebResource alloc] _initWithCoreResource:subresource.get()] autorelease] : nil; +} + +- (void)addSubresource:(WebResource *)subresource +{ + _private->loader->addArchiveResource([subresource _coreResource]); +} + +@end diff --git a/Source/WebKit/mac/WebView/WebDataSourceInternal.h b/Source/WebKit/mac/WebView/WebDataSourceInternal.h new file mode 100644 index 0000000..29497d5 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDataSourceInternal.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. + * + * 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 "WebDataSourcePrivate.h" +#import <wtf/Forward.h> + +namespace WebCore { + class DocumentLoader; +} + +class WebDocumentLoaderMac; + +@class DOMDocumentFragment; +@class DOMElement; +@class WebArchive; +@class WebResource; +@class WebView; + +@interface WebDataSource (WebInternal) +- (void)_makeRepresentation; +- (BOOL)_isDocumentHTML; +- (WebView *)_webView; +- (NSURL *)_URL; +- (DOMElement *)_imageElementWithImageResource:(WebResource *)resource; +- (DOMDocumentFragment *)_documentFragmentWithImageResource:(WebResource *)resource; +- (DOMDocumentFragment *)_documentFragmentWithArchive:(WebArchive *)archive; ++ (NSMutableDictionary *)_repTypesAllowImageTypeOmission:(BOOL)allowImageTypeOmission; +- (void)_replaceSelectionWithArchive:(WebArchive *)archive selectReplacement:(BOOL)selectReplacement; +- (id)_initWithDocumentLoader:(PassRefPtr<WebDocumentLoaderMac>)loader; +- (void)_finishedLoading; +- (void)_receivedData:(NSData *)data; +- (void)_revertToProvisionalState; +- (void)_setMainDocumentError:(NSError *)error; +- (WebCore::DocumentLoader*)_documentLoader; +@end diff --git a/Source/WebKit/mac/WebView/WebDataSourcePrivate.h b/Source/WebKit/mac/WebView/WebDataSourcePrivate.h new file mode 100644 index 0000000..df9c61e --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDataSourcePrivate.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * 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 <WebKit/WebDataSource.h> + +@interface WebDataSource (WebPrivate) + +- (NSFileWrapper *)_fileWrapperForURL:(NSURL *)URL; +- (void)_addSubframeArchives:(NSArray *) archives; +- (NSError *)_mainDocumentError; +- (NSString *)_responseMIMEType; + +- (BOOL)_transferApplicationCache:(NSString*)destinationBundleIdentifier; + +- (void)_setDeferMainResourceDataLoad:(BOOL)flag; + +@end diff --git a/Source/WebKit/mac/WebView/WebDelegateImplementationCaching.h b/Source/WebKit/mac/WebView/WebDelegateImplementationCaching.h new file mode 100644 index 0000000..2aadc83 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDelegateImplementationCaching.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * 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. + */ + +// This header contains WebView declarations that can be used anywhere in WebKit, but are neither SPI nor API. + +#import "WebTypesInternal.h" + +@class WebView; + +struct WebResourceDelegateImplementationCache { + IMP didCancelAuthenticationChallengeFunc; + IMP didReceiveAuthenticationChallengeFunc; +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + IMP canAuthenticateAgainstProtectionSpaceFunc; +#endif + IMP identifierForRequestFunc; + IMP willSendRequestFunc; + IMP didReceiveResponseFunc; + IMP didReceiveContentLengthFunc; + IMP didFinishLoadingFromDataSourceFunc; + IMP didFailLoadingWithErrorFromDataSourceFunc; + IMP didLoadResourceFromMemoryCacheFunc; + IMP willCacheResponseFunc; + IMP plugInFailedWithErrorFunc; + IMP shouldUseCredentialStorageFunc; +}; + +struct WebFrameLoadDelegateImplementationCache { + IMP didClearWindowObjectForFrameFunc; + IMP didClearWindowObjectForFrameInScriptWorldFunc; + IMP didClearInspectorWindowObjectForFrameFunc; + IMP windowScriptObjectAvailableFunc; + IMP didHandleOnloadEventsForFrameFunc; + IMP didReceiveServerRedirectForProvisionalLoadForFrameFunc; + IMP didCancelClientRedirectForFrameFunc; + IMP willPerformClientRedirectToURLDelayFireDateForFrameFunc; + IMP didChangeLocationWithinPageForFrameFunc; + IMP didPushStateWithinPageForFrameFunc; + IMP didReplaceStateWithinPageForFrameFunc; + IMP didPopStateWithinPageForFrameFunc; + IMP willCloseFrameFunc; + IMP didStartProvisionalLoadForFrameFunc; + IMP didReceiveTitleForFrameFunc; + IMP didCommitLoadForFrameFunc; + IMP didFailProvisionalLoadWithErrorForFrameFunc; + IMP didFailLoadWithErrorForFrameFunc; + IMP didFinishLoadForFrameFunc; + IMP didFirstLayoutInFrameFunc; + IMP didFirstVisuallyNonEmptyLayoutInFrameFunc; + IMP didReceiveIconForFrameFunc; + IMP didFinishDocumentLoadForFrameFunc; + IMP didDisplayInsecureContentFunc; + IMP didRunInsecureContentFunc; +}; + +struct WebScriptDebugDelegateImplementationCache { + BOOL didParseSourceExpectsBaseLineNumber; + IMP didParseSourceFunc; + IMP failedToParseSourceFunc; + IMP didEnterCallFrameFunc; + IMP willExecuteStatementFunc; + IMP willLeaveCallFrameFunc; + IMP exceptionWasRaisedFunc; +}; + +struct WebHistoryDelegateImplementationCache { + IMP navigatedFunc; + IMP clientRedirectFunc; + IMP serverRedirectFunc; + IMP setTitleFunc; + IMP populateVisitedLinksFunc; +}; + +WebResourceDelegateImplementationCache* WebViewGetResourceLoadDelegateImplementations(WebView *); +WebFrameLoadDelegateImplementationCache* WebViewGetFrameLoadDelegateImplementations(WebView *); +WebScriptDebugDelegateImplementationCache* WebViewGetScriptDebugDelegateImplementations(WebView *); +WebHistoryDelegateImplementationCache* WebViewGetHistoryDelegateImplementations(WebView *webView); + +id CallFormDelegate(WebView *, SEL, id, id); +id CallFormDelegate(WebView *self, SEL selector, id object1, id object2, id object3, id object4, id object5); +BOOL CallFormDelegateReturningBoolean(BOOL, WebView *, SEL, id, SEL, id); + +id CallUIDelegate(WebView *, SEL); +id CallUIDelegate(WebView *, SEL, id); +id CallUIDelegate(WebView *, SEL, NSRect); +id CallUIDelegate(WebView *, SEL, id, id); +id CallUIDelegate(WebView *, SEL, id, BOOL); +id CallUIDelegate(WebView *, SEL, id, id, id); +id CallUIDelegate(WebView *, SEL, id, NSUInteger); +float CallUIDelegateReturningFloat(WebView *, SEL); +BOOL CallUIDelegateReturningBoolean(BOOL, WebView *, SEL); +BOOL CallUIDelegateReturningBoolean(BOOL, WebView *, SEL, id); +BOOL CallUIDelegateReturningBoolean(BOOL, WebView *, SEL, id, id); +BOOL CallUIDelegateReturningBoolean(BOOL, WebView *, SEL, id, BOOL); +BOOL CallUIDelegateReturningBoolean(BOOL, WebView *, SEL, id, BOOL, id); + +id CallFrameLoadDelegate(IMP, WebView *, SEL); +id CallFrameLoadDelegate(IMP, WebView *, SEL, id); +id CallFrameLoadDelegate(IMP, WebView *, SEL, id, id); +id CallFrameLoadDelegate(IMP, WebView *, SEL, id, id, id); +id CallFrameLoadDelegate(IMP, WebView *, SEL, id, id, id, id); +id CallFrameLoadDelegate(IMP, WebView *, SEL, id, NSTimeInterval, id, id); + +id CallResourceLoadDelegate(IMP, WebView *, SEL, id, id); +id CallResourceLoadDelegate(IMP, WebView *, SEL, id, id, id); +id CallResourceLoadDelegate(IMP, WebView *, SEL, id, id, id, id); +id CallResourceLoadDelegate(IMP, WebView *, SEL, id, NSInteger, id); +id CallResourceLoadDelegate(IMP, WebView *, SEL, id, id, NSInteger, id); + +BOOL CallResourceLoadDelegateReturningBoolean(BOOL, IMP, WebView *, SEL, id, id); +BOOL CallResourceLoadDelegateReturningBoolean(BOOL, IMP, WebView *, SEL, id, id, id); + +id CallScriptDebugDelegate(IMP, WebView *, SEL, id, id, NSInteger, id); +id CallScriptDebugDelegate(IMP, WebView *, SEL, id, NSInteger, id, NSInteger, id); +id CallScriptDebugDelegate(IMP, WebView *, SEL, id, NSInteger, id, id, id); +id CallScriptDebugDelegate(IMP, WebView *, SEL, id, NSInteger, NSInteger, id); + +id CallHistoryDelegate(IMP, WebView *, SEL); +id CallHistoryDelegate(IMP, WebView *, SEL, id, id); +id CallHistoryDelegate(IMP, WebView *, SEL, id, id, id); diff --git a/Source/WebKit/mac/WebView/WebDelegateImplementationCaching.mm b/Source/WebKit/mac/WebView/WebDelegateImplementationCaching.mm new file mode 100644 index 0000000..d00d60b --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDelegateImplementationCaching.mm @@ -0,0 +1,644 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009 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 "WebDelegateImplementationCaching.h" + +#import "WebKitLogging.h" +#import "WebView.h" +#import "WebViewData.h" +#import <objc/objc-runtime.h> + +@implementation WebView (WebDelegateImplementationCaching) + +WebResourceDelegateImplementationCache* WebViewGetResourceLoadDelegateImplementations(WebView *webView) +{ + static WebResourceDelegateImplementationCache empty; + if (!webView) + return ∅ + return &webView->_private->resourceLoadDelegateImplementations; +} + +WebFrameLoadDelegateImplementationCache* WebViewGetFrameLoadDelegateImplementations(WebView *webView) +{ + static WebFrameLoadDelegateImplementationCache empty; + if (!webView) + return ∅ + return &webView->_private->frameLoadDelegateImplementations; +} + +WebScriptDebugDelegateImplementationCache* WebViewGetScriptDebugDelegateImplementations(WebView *webView) +{ + static WebScriptDebugDelegateImplementationCache empty; + if (!webView) + return ∅ + return &webView->_private->scriptDebugDelegateImplementations; +} + +WebHistoryDelegateImplementationCache* WebViewGetHistoryDelegateImplementations(WebView *webView) +{ + static WebHistoryDelegateImplementationCache empty; + if (!webView) + return ∅ + return &webView->_private->historyDelegateImplementations; +} + +// 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. + +typedef float (*ObjCMsgSendFPRet)(id, SEL, ...); +#if defined(__i386__) +static const ObjCMsgSendFPRet objc_msgSend_float_return = reinterpret_cast<ObjCMsgSendFPRet>(objc_msgSend_fpret); +#else +static const ObjCMsgSendFPRet objc_msgSend_float_return = 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 objc_msgSend_float_return(delegate, selector, self); + @try { + return objc_msgSend_float_return(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 object, BOOL boolean, id object2) +{ + if (!delegate || ![delegate respondsToSelector:selector]) + return result; + if (!self->_private->catchesDelegateExceptions) + return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id, BOOL, id)>(objc_msgSend)(delegate, selector, self, object, boolean, object2); + @try { + return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id, BOOL, id)>(objc_msgSend)(delegate, selector, self, object, boolean, object2); + } @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, NSInteger integer1, NSInteger integer2, id object2) +{ + if (!delegate) + return nil; + if (!self->_private->catchesDelegateExceptions) + return implementation(delegate, selector, self, object1, integer1, integer2, object2); + @try { + return implementation(delegate, selector, self, object1, integer1, integer2, 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, NSInteger integer1, id object2, NSInteger integer2, id object3) +{ + if (!delegate) + return nil; + if (!self->_private->catchesDelegateExceptions) + return implementation(delegate, selector, self, object1, integer1, object2, integer2, object3); + @try { + return implementation(delegate, selector, self, object1, integer1, object2, integer2, object3); + } @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, id object3, id object4) +{ + if (!delegate) + return nil; + if (!self->_private->catchesDelegateExceptions) + return implementation(delegate, selector, self, object1, integer, object2, object3, object4); + @try { + return implementation(delegate, selector, self, object1, integer, 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, 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 object, BOOL boolean, id object2) +{ + return CallDelegateReturningBoolean(result, self, self->_private->UIDelegate, selector, object, boolean, object2); +} + +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); +} + +BOOL CallResourceLoadDelegateReturningBoolean(BOOL result, IMP implementation, WebView *self, SEL selector, id object1, id object2) +{ + if (!self->_private->catchesDelegateExceptions) + return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id, id)>(objc_msgSend)(self->_private->resourceProgressDelegate, selector, self, object1, object2); + @try { + return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id, id)>(objc_msgSend)(self->_private->resourceProgressDelegate, selector, self, object1, object2); + } @catch(id exception) { + ReportDiscardedDelegateException(selector, exception); + } + return result; +} + +BOOL CallResourceLoadDelegateReturningBoolean(BOOL result, IMP implementation, WebView *self, SEL selector, id object1, id object2, id object3) +{ + if (!self->_private->catchesDelegateExceptions) + return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id, id, id)>(objc_msgSend)(self->_private->resourceProgressDelegate, selector, self, object1, object2, object3); + @try { + return reinterpret_cast<BOOL (*)(id, SEL, WebView *, id, id, id)>(objc_msgSend)(self->_private->resourceProgressDelegate, selector, self, object1, object2, object3); + } @catch(id exception) { + ReportDiscardedDelegateException(selector, exception); + } + return result; +} + +id CallScriptDebugDelegate(IMP implementation, WebView *self, SEL selector, id object1, id object2, NSInteger integer, id object3) +{ + return CallDelegate(implementation, self, self->_private->scriptDebugDelegate, selector, object1, object2, integer, object3); +} + +id CallScriptDebugDelegate(IMP implementation, WebView *self, SEL selector, id object1, NSInteger integer1, id object2, NSInteger integer2, id object3) +{ + return CallDelegate(implementation, self, self->_private->scriptDebugDelegate, selector, object1, integer1, object2, integer2, object3); +} + +id CallScriptDebugDelegate(IMP implementation, WebView *self, SEL selector, id object1, NSInteger integer, id object2, id object3, id object4) +{ + return CallDelegate(implementation, self, self->_private->scriptDebugDelegate, selector, object1, integer, object2, object3, object4); +} + +id CallScriptDebugDelegate(IMP implementation, WebView *self, SEL selector, id object1, NSInteger integer1, NSInteger integer2, id object2) +{ + return CallDelegate(implementation, self, self->_private->scriptDebugDelegate, selector, object1, integer1, integer2, object2); +} + +id CallHistoryDelegate(IMP implementation, WebView *self, SEL selector) +{ + return CallDelegate(implementation, self, self->_private->historyDelegate, selector); +} + +id CallHistoryDelegate(IMP implementation, WebView *self, SEL selector, id object1, id object2) +{ + return CallDelegate(implementation, self, self->_private->historyDelegate, selector, object1, object2); +} + +id CallHistoryDelegate(IMP implementation, WebView *self, SEL selector, id object1, id object2, id object3) +{ + return CallDelegate(implementation, self, self->_private->historyDelegate, selector, object1, object2, 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 diff --git a/Source/WebKit/mac/WebView/WebDeviceOrientation.h b/Source/WebKit/mac/WebView/WebDeviceOrientation.h new file mode 100644 index 0000000..ee04199 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDeviceOrientation.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER OR + * 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. + */ + + +@class WebDeviceOrientationInternal; + +@interface WebDeviceOrientation : NSObject { + WebDeviceOrientationInternal* m_internal; +} + +- (id)initWithCanProvideAlpha:(bool)canProvideAlpha alpha:(double)alpha canProvideBeta:(bool)canProvideBeta beta:(double)beta canProvideGamma:(bool)canProvideGamma gamma:(double)gamma; + +@end diff --git a/Source/WebKit/mac/WebView/WebDeviceOrientation.mm b/Source/WebKit/mac/WebView/WebDeviceOrientation.mm new file mode 100644 index 0000000..7be5d0c --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDeviceOrientation.mm @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER OR + * 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 "WebDeviceOrientationInternal.h" + +using namespace WebCore; + +@implementation WebDeviceOrientationInternal + +- (id)initWithCoreDeviceOrientation:(PassRefPtr<DeviceOrientation>)coreDeviceOrientation +{ + self = [super init]; + if (!self) + return nil; + m_orientation = coreDeviceOrientation; + return self; +} + +@end + +@implementation WebDeviceOrientation (Internal) + +- (id)initWithCoreDeviceOrientation:(PassRefPtr<WebCore::DeviceOrientation>)coreDeviceOrientation +{ + self = [super init]; + if (!self) + return nil; + m_internal = [[WebDeviceOrientationInternal alloc] initWithCoreDeviceOrientation:coreDeviceOrientation]; + return self; +} + +@end + +@implementation WebDeviceOrientation + +DeviceOrientation* core(WebDeviceOrientation* orientation) +{ + return orientation ? orientation->m_internal->m_orientation.get() : 0; +} + +- (id)initWithCanProvideAlpha:(bool)canProvideAlpha alpha:(double)alpha canProvideBeta:(bool)canProvideBeta beta:(double)beta canProvideGamma:(bool)canProvideGamma gamma:(double)gamma +{ + self = [super init]; + if (!self) + return nil; + m_internal = [[WebDeviceOrientationInternal alloc] initWithCoreDeviceOrientation:DeviceOrientation::create(canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma)]; + return self; +} + +- (void)dealloc +{ + [m_internal release]; + [super dealloc]; +} + +@end diff --git a/Source/WebKit/mac/WebView/WebDeviceOrientationInternal.h b/Source/WebKit/mac/WebView/WebDeviceOrientationInternal.h new file mode 100644 index 0000000..b7c6aa5 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDeviceOrientationInternal.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER OR + * 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 "WebDeviceOrientation.h" + +#import <WebCore/DeviceOrientation.h> +#import <wtf/RefPtr.h> + +@interface WebDeviceOrientationInternal : NSObject { +@public + RefPtr<WebCore::DeviceOrientation> m_orientation; +} + +- (id)initWithCoreDeviceOrientation:(PassRefPtr<WebCore::DeviceOrientation>)coreDeviceOrientation; +@end + +@interface WebDeviceOrientation (Internal) + +- (id)initWithCoreDeviceOrientation:(PassRefPtr<WebCore::DeviceOrientation>)coreDeviceOrientation; + +@end + +WebCore::DeviceOrientation* core(WebDeviceOrientation*); diff --git a/Source/WebKit/mac/WebView/WebDeviceOrientationProvider.h b/Source/WebKit/mac/WebView/WebDeviceOrientationProvider.h new file mode 100644 index 0000000..18b688c --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDeviceOrientationProvider.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER OR + * 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. + */ + +@class WebDeviceOrientation; + +@protocol WebDeviceOrientationProvider <NSObject> +- (void)startUpdating; +- (void)stopUpdating; +- (WebDeviceOrientation*)lastOrientation; +@end diff --git a/Source/WebKit/mac/WebView/WebDeviceOrientationProviderMock.h b/Source/WebKit/mac/WebView/WebDeviceOrientationProviderMock.h new file mode 100644 index 0000000..8f33691 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDeviceOrientationProviderMock.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER OR + * 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 "WebDeviceOrientationProvider.h" + +@class WebDeviceOrientationProviderMockInternal; +@class WebDeviceOrientation; + +@interface WebDeviceOrientationProviderMock : NSObject<WebDeviceOrientationProvider> { + WebDeviceOrientationProviderMockInternal* m_internal; +} + ++ (WebDeviceOrientationProviderMock *)shared; + +- (id)init; +- (void)setOrientation:(WebDeviceOrientation*)orientation; + +@end diff --git a/Source/WebKit/mac/WebView/WebDeviceOrientationProviderMock.mm b/Source/WebKit/mac/WebView/WebDeviceOrientationProviderMock.mm new file mode 100644 index 0000000..9600b7d --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDeviceOrientationProviderMock.mm @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER OR + * 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 "WebDeviceOrientationProviderMockInternal.h" + +#import "WebDeviceOrientationInternal.h" + +using namespace WebCore; + +@implementation WebDeviceOrientationProviderMockInternal + +- (id)init +{ + self = [super init]; + if (!self) + return nil; + m_core.set(new DeviceOrientationClientMock()); + return self; +} + +- (void)setOrientation:(WebDeviceOrientation*)orientation +{ + m_core->setOrientation(core(orientation)); +} + +- (void)setController:(DeviceOrientationController*)controller +{ + m_core->setController(controller); +} + +- (void)startUpdating +{ + m_core->startUpdating(); +} + +- (void)stopUpdating +{ + m_core->stopUpdating(); +} + +- (WebDeviceOrientation*)lastOrientation +{ + return [[WebDeviceOrientation alloc] initWithCoreDeviceOrientation:m_core->lastOrientation()]; +} + +@end + +@implementation WebDeviceOrientationProviderMock (Internal) + +- (void)setController:(WebCore::DeviceOrientationController*)controller +{ + [m_internal setController:controller]; +} + +@end + +@implementation WebDeviceOrientationProviderMock + ++ (WebDeviceOrientationProviderMock *)shared +{ + static WebDeviceOrientationProviderMock *provider = [[WebDeviceOrientationProviderMock alloc] init]; + return provider; +} + +- (id)init +{ + self = [super init]; + if (!self) + return nil; + m_internal = [[WebDeviceOrientationProviderMockInternal alloc] init]; + return self; +} + +- (void)dealloc +{ + [super dealloc]; + [m_internal release]; +} + +- (void)setOrientation:(WebDeviceOrientation*)orientation +{ + [m_internal setOrientation:orientation]; +} + +- (void)startUpdating +{ + [m_internal startUpdating]; +} + +- (void)stopUpdating +{ + [m_internal stopUpdating]; +} + +- (WebDeviceOrientation*)lastOrientation +{ + return [m_internal lastOrientation]; +} + +@end diff --git a/Source/WebKit/mac/WebView/WebDeviceOrientationProviderMockInternal.h b/Source/WebKit/mac/WebView/WebDeviceOrientationProviderMockInternal.h new file mode 100644 index 0000000..5ac38e5 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDeviceOrientationProviderMockInternal.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER OR + * 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 "WebDeviceOrientationProviderMock.h" + +#import <WebCore/DeviceOrientationClientMock.h> +#import <wtf/OwnPtr.h> + +@interface WebDeviceOrientationProviderMockInternal : NSObject { + OwnPtr<WebCore::DeviceOrientationClientMock> m_core; +} + +- (id)init; + +- (void)setOrientation:(WebDeviceOrientation*)orientation; + +- (void)setController:(WebCore::DeviceOrientationController*)controller; + +- (void)startUpdating; +- (void)stopUpdating; +- (WebDeviceOrientation*)lastOrientation; +@end + +@interface WebDeviceOrientationProviderMock (Internal) +- (void)setController:(WebCore::DeviceOrientationController*)controller; +@end diff --git a/Source/WebKit/mac/WebView/WebDocument.h b/Source/WebKit/mac/WebView/WebDocument.h new file mode 100644 index 0000000..b18215a --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDocument.h @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2003, 2004, 2005 Apple Computer, Inc. All rights reserved. + * + * 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 <Cocoa/Cocoa.h> + +@class NSError; +@class WebDataSource; + +/*! + @protocol WebDocumentView + @discussion Protocol implemented by the document view of WebFrameView +*/ +@protocol WebDocumentView <NSObject> + +/*! + @method setDataSource: + @abstract Called when the corresponding data source has been created. + @param dataSource The corresponding data source. +*/ +- (void)setDataSource:(WebDataSource *)dataSource; + +/*! + @method dataSourceUpdated: + @abstract Called when the corresponding data source has received data. + @param dataSource The corresponding data source. +*/ +- (void)dataSourceUpdated:(WebDataSource *)dataSource; + +/*! + @method setNeedsLayout: + @discussion Called when WebKit has determined that the document view needs to layout. + This method should simply set a flag and call layout from drawRect if the flag is YES. + @param flag YES to cause a layout, no to not cause a layout. +*/ +- (void)setNeedsLayout:(BOOL)flag; + +/*! + @method layout + @discussion Called when the document view must immediately layout. For simple views, + setting the frame is a sufficient implementation of this method. +*/ +- (void)layout; + +/*! + @method viewWillMoveToHostWindow: + @param hostWindow The host window for the document view. + @abstract Called before the host window is set on the parent web view. +*/ +- (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow; + +/*! + @method viewDidMoveToHostWindow + @abstract Called after the host window is set on the parent web view. +*/ +- (void)viewDidMoveToHostWindow; + +@end + + +/*! + @protocol WebDocumentSearching + @discussion Optional protocol for searching document view of WebFrameView. +*/ +@protocol WebDocumentSearching <NSObject> +/*! + @method searchFor:direction:caseSensitive:wrap: + @abstract Searches a document view for a string and highlights the string if it is found. + @param string The string to search for. + @param forward YES to search forward, NO to seach backwards. + @param caseFlag YES to for case-sensitive search, NO for case-insensitive search. + @param wrapFlag YES to wrap around, NO to avoid wrapping. + @result YES if found, NO if not found. +*/ +- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag; +@end + + +/*! + @protocol WebDocumentText + @discussion Optional protocol for supporting text operations. +*/ +@protocol WebDocumentText <NSObject> + +/*! + @method supportsTextEncoding + @result YES if the document view support text encoding, NO if it doesn't. +*/ +- (BOOL)supportsTextEncoding; + +/*! + @method string + @result String that represents the entire document. +*/ +- (NSString *)string; + +/*! + @method attributedString + @result Attributed string that represents the entire document. +*/ +- (NSAttributedString *)attributedString; + +/*! + @method selectedString + @result String that represents the current selection. +*/ +- (NSString *)selectedString; + +/*! + @method selectedAttributedString + @result Attributed string that represents the current selection. +*/ +- (NSAttributedString *)selectedAttributedString; + + +/*! + @method selectAll + @abstract Selects all the text in the document. +*/ +- (void)selectAll; + +/*! + @method deselectText + @abstract Causes a text selection to lose its selection. +*/ +- (void)deselectAll; + +@end + + +/*! + @protocol WebDocumentRepresentation + @discussion Protocol implemented by the document representation of a data source. +*/ +@protocol WebDocumentRepresentation <NSObject> +/*! + @method setDataSource: + @abstract Called soon after the document representation is created. + @param dataSource The data source that is set. +*/ +- (void)setDataSource:(WebDataSource *)dataSource; + +/*! + @method receivedData:withDataSource: + @abstract Called when the data source has received data. + @param data The data that the data source has received. + @param dataSource The data source that has received data. +*/ +- (void)receivedData:(NSData *)data withDataSource:(WebDataSource *)dataSource; + +/*! + @method receivedError:withDataSource: + @abstract Called when the data source has received an error. + @param error The error that the data source has received. + @param dataSource The data source that has received the error. +*/ +- (void)receivedError:(NSError *)error withDataSource:(WebDataSource *)dataSource; + +/*! + @method finishedLoadingWithDataSource: + @abstract Called when the data source has finished loading. + @param dataSource The datasource that has finished loading. +*/ +- (void)finishedLoadingWithDataSource:(WebDataSource *)dataSource; + +/*! + @method canProvideDocumentSource + @result Returns true if the representation can provide document source. +*/ +- (BOOL)canProvideDocumentSource; + +/*! + @method documentSource + @result Returns the textual source representation of the document. For HTML documents + this is the original HTML source. +*/ +- (NSString *)documentSource; + +/*! + @method title + @result Return the title for the document. +*/ +- (NSString *)title; + +@end diff --git a/Source/WebKit/mac/WebView/WebDocumentInternal.h b/Source/WebKit/mac/WebView/WebDocumentInternal.h new file mode 100644 index 0000000..bcb6c07 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDocumentInternal.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * 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 <WebKit/WebDocumentPrivate.h> +#import <WebKit/WebHTMLView.h> +#import <WebKit/WebViewPrivate.h> + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 +#define WebNSUInteger unsigned int +#else +#define WebNSUInteger NSUInteger +#endif + +/*! +@protocol _WebDocumentZooming +@discussion Optional protocol for a view that wants to handle its own zoom. +*/ +@protocol _WebDocumentZooming <NSObject> + +// Methods to perform the actual commands +- (IBAction)_zoomOut:(id)sender; +- (IBAction)_zoomIn:(id)sender; +- (IBAction)_resetZoom:(id)sender; + +// Whether or not the commands can be executed. +- (BOOL)_canZoomOut; +- (BOOL)_canZoomIn; +- (BOOL)_canResetZoom; + +@end + +@protocol WebDocumentElement <NSObject> +- (NSDictionary *)elementAtPoint:(NSPoint)point; +- (NSDictionary *)elementAtPoint:(NSPoint)point allowShadowContent:(BOOL)allow; +@end + +@protocol WebMultipleTextMatches <NSObject> +- (void)setMarkedTextMatchesAreHighlighted:(BOOL)newValue; +- (BOOL)markedTextMatchesAreHighlighted; +- (WebNSUInteger)countMatchesForText:(NSString *)string inDOMRange:(DOMRange *)range options:(WebFindOptions)options limit:(WebNSUInteger)limit markMatches:(BOOL)markMatches; +- (void)unmarkAllTextMatches; +- (NSArray *)rectsForTextMatches; +@end + +@protocol WebDocumentOptionsSearching <NSObject> +// Prefixed with an underscore to avoid conflict with Mail's -[WebHTMLView(MailExtras) findString:options:]. +- (BOOL)_findString:(NSString *)string options:(WebFindOptions)options; +@end + +/* Used to save and restore state in the view, typically when going back/forward */ +@protocol _WebDocumentViewState <NSObject> +- (NSPoint)scrollPoint; +- (void)setScrollPoint:(NSPoint)p; +- (id)viewState; +- (void)setViewState:(id)statePList; +@end + +@interface WebHTMLView (WebDocumentInternalProtocols) <WebDocumentElement, WebMultipleTextMatches, WebDocumentOptionsSearching> +@end + +#undef WebNSUInteger diff --git a/Source/WebKit/mac/WebView/WebDocumentLoaderMac.h b/Source/WebKit/mac/WebView/WebDocumentLoaderMac.h new file mode 100644 index 0000000..83e2bb3 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDocumentLoaderMac.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * 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 <WebCore/DocumentLoader.h> +#import <wtf/RetainPtr.h> +#import <wtf/HashSet.h> + +@class WebDataSource; +@class WebView; + +namespace WebCore { + class ResourceRequest; +} + +class WebDocumentLoaderMac : public WebCore::DocumentLoader { +public: + static PassRefPtr<WebDocumentLoaderMac> create(const WebCore::ResourceRequest& request, const WebCore::SubstituteData& data) + { + return adoptRef(new WebDocumentLoaderMac(request, data)); + } + + void setDataSource(WebDataSource *, WebView*); + void detachDataSource(); + WebDataSource *dataSource() const; + + void increaseLoadCount(unsigned long identifier); + void decreaseLoadCount(unsigned long identifier); + +private: + WebDocumentLoaderMac(const WebCore::ResourceRequest&, const WebCore::SubstituteData&); + + virtual void attachToFrame(); + virtual void detachFromFrame(); + + void retainDataSource(); + void releaseDataSource(); + + WebDataSource *m_dataSource; + bool m_isDataSourceRetained; + RetainPtr<id> m_resourceLoadDelegate; + RetainPtr<id> m_downloadDelegate; + HashSet<unsigned long> m_loadingResources; +}; diff --git a/Source/WebKit/mac/WebView/WebDocumentLoaderMac.mm b/Source/WebKit/mac/WebView/WebDocumentLoaderMac.mm new file mode 100644 index 0000000..d5812a4 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDocumentLoaderMac.mm @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * 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 "WebDocumentLoaderMac.h" + +#import "WebKitVersionChecks.h" +#import "WebView.h" + +using namespace WebCore; + +WebDocumentLoaderMac::WebDocumentLoaderMac(const ResourceRequest& request, const SubstituteData& substituteData) + : DocumentLoader(request, substituteData) + , m_dataSource(nil) + , m_isDataSourceRetained(false) +{ +} + +static inline bool needsDataLoadWorkaround(WebView *webView) +{ +#ifdef BUILDING_ON_TIGER + // Tiger has to be a little less efficient. + id frameLoadDelegate = [webView frameLoadDelegate]; + if (!frameLoadDelegate) + return false; + + NSString *bundleIdentifier = [[NSBundle bundleForClass:[frameLoadDelegate class]] bundleIdentifier]; + + if ([bundleIdentifier isEqualToString:@"com.apple.AppKit"]) + return true; + if ([bundleIdentifier isEqualToString:@"com.adobe.Installers.Setup"]) + return true; + return false; +#else + static bool needsWorkaround = !WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_ADOBE_INSTALLER_QUIRK) + && [[[NSBundle mainBundle] bundleIdentifier] isEqualToString:@"com.adobe.Installers.Setup"]; + return needsWorkaround; +#endif +} + +void WebDocumentLoaderMac::setDataSource(WebDataSource *dataSource, WebView *webView) +{ + ASSERT(!m_dataSource); + ASSERT(!m_isDataSourceRetained); + + m_dataSource = dataSource; + retainDataSource(); + + m_resourceLoadDelegate = [webView resourceLoadDelegate]; + m_downloadDelegate = [webView downloadDelegate]; + + // Some clients run the run loop in a way that prevents the data load timer + // from firing. We work around that issue here. See <rdar://problem/5266289> + // and <rdar://problem/5049509>. + if (needsDataLoadWorkaround(webView)) + m_deferMainResourceDataLoad = false; +} + +WebDataSource *WebDocumentLoaderMac::dataSource() const +{ + return m_dataSource; +} + +void WebDocumentLoaderMac::attachToFrame() +{ + DocumentLoader::attachToFrame(); + + retainDataSource(); +} + +void WebDocumentLoaderMac::detachFromFrame() +{ + DocumentLoader::detachFromFrame(); + + if (m_loadingResources.isEmpty()) + releaseDataSource(); + + // FIXME: What prevents the data source from getting deallocated while the + // frame is not attached? +} + +void WebDocumentLoaderMac::increaseLoadCount(unsigned long identifier) +{ + ASSERT(m_dataSource); + + if (m_loadingResources.contains(identifier)) + return; + m_loadingResources.add(identifier); + + retainDataSource(); +} + +void WebDocumentLoaderMac::decreaseLoadCount(unsigned long identifier) +{ + HashSet<unsigned long>::iterator it = m_loadingResources.find(identifier); + + // It is valid for a load to be cancelled before it's started. + if (it == m_loadingResources.end()) + return; + + m_loadingResources.remove(it); + + if (m_loadingResources.isEmpty()) { + m_resourceLoadDelegate = 0; + m_downloadDelegate = 0; + if (!frame()) + releaseDataSource(); + } +} + +void WebDocumentLoaderMac::retainDataSource() +{ + if (m_isDataSourceRetained || !m_dataSource) + return; + m_isDataSourceRetained = true; + CFRetain(m_dataSource); +} + +void WebDocumentLoaderMac::releaseDataSource() +{ + if (!m_isDataSourceRetained) + return; + ASSERT(m_dataSource); + m_isDataSourceRetained = false; + CFRelease(m_dataSource); +} + +void WebDocumentLoaderMac::detachDataSource() +{ + ASSERT(!m_isDataSourceRetained); + m_dataSource = nil; +} diff --git a/Source/WebKit/mac/WebView/WebDocumentPrivate.h b/Source/WebKit/mac/WebView/WebDocumentPrivate.h new file mode 100644 index 0000000..a495e4b --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDocumentPrivate.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * 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 <WebKit/WebDocument.h> +#import <WebKit/WebHTMLView.h> + +@class DOMDocument; +@class PDFDocument; + +@protocol WebDocumentImage <NSObject> +- (NSImage *)image; +@end + +// This method is deprecated as it now lives on WebFrame. +@protocol WebDocumentDOM <NSObject> +- (DOMDocument *)DOMDocument; +- (BOOL)canSaveAsWebArchive; +@end + +@protocol WebDocumentSelection <WebDocumentText> +- (NSArray *)pasteboardTypesForSelection; +- (void)writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard; + +// Array of rects that tightly enclose the selected text, in coordinates of selectinView. +- (NSArray *)selectionTextRects; + +// Rect tightly enclosing the entire selected area, in coordinates of selectionView. +- (NSRect)selectionRect; + +// NSImage of the portion of the selection that's in view. This does not draw backgrounds. +// The text is all black according to the parameter. +- (NSImage *)selectionImageForcingBlackText:(BOOL)forceBlackText; + +// Rect tightly enclosing the entire selected area, in coordinates of selectionView. +// NOTE: This method is equivalent to selectionRect and shouldn't be used; use selectionRect instead. +- (NSRect)selectionImageRect; + +// View that draws the selection and can be made first responder. Often this is self but it could be +// a nested view, as for example in the case of WebPDFView. +- (NSView *)selectionView; +@end + +@protocol WebDocumentPDF <WebDocumentText> +- (PDFDocument *)PDFDocument; +@end + +@protocol WebDocumentIncrementalSearching +/*! +@method searchFor:direction:caseSensitive:wrap:startInSelection: + @abstract Searches a document view for a string and highlights the string if it is found. + @param string The string to search for. + @param forward YES to search forward, NO to seach backwards. + @param caseFlag YES to for case-sensitive search, NO for case-insensitive search. + @param wrapFlag YES to wrap around, NO to avoid wrapping. + @param startInSelection YES to begin search in the selected text (useful for incremental searching), NO to begin search after the selected text. + @result YES if found, NO if not found. + */ +- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection; +@end + +@interface WebHTMLView (WebDocumentPrivateProtocols) <WebDocumentSelection, WebDocumentIncrementalSearching> +@end diff --git a/Source/WebKit/mac/WebView/WebDynamicScrollBarsView.h b/Source/WebKit/mac/WebView/WebDynamicScrollBarsView.h new file mode 100644 index 0000000..b89462d --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDynamicScrollBarsView.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2005, 2008, 2010 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +// This is a Private header (containing SPI), despite the fact that its name +// does not contain the word Private. + +#import <AppKit/NSScrollView.h> + +// FIXME: <rdar://problem/5898985> Mail currently expects this header to define WebCoreScrollbarAlwaysOn. +extern const int WebCoreScrollbarAlwaysOn; + +struct WebDynamicScrollBarsViewPrivate; +@interface WebDynamicScrollBarsView : NSScrollView { +@private + struct WebDynamicScrollBarsViewPrivate *_private; + +#ifndef __OBJC2__ + // We need to pad the class out to its former size. See <rdar://problem/7814899> for more information. + char padding[16]; +#endif +} + +// This was originally added for Safari's benefit, but Safari has not used it for a long time. +// Perhaps it can be removed. +- (void)setAllowsHorizontalScrolling:(BOOL)flag; + +// Determines whether the scrollers should be drawn outside of the content (as in normal scroll views) +// or should overlap the content. +- (void)setAllowsScrollersToOverlapContent:(BOOL)flag; + +// These methods hide the scrollers in a way that does not prevent scrolling. +- (void)setAlwaysHideHorizontalScroller:(BOOL)flag; +- (void)setAlwaysHideVerticalScroller:(BOOL)flag; + +// These methods return YES if the scrollers are visible, or if the only reason that they are not +// visible is that they have been suppressed by setAlwaysHideHorizontal/VerticalScroller:. +- (BOOL)horizontalScrollingAllowed; +- (BOOL)verticalScrollingAllowed; + +// Returns YES if we're currently in the middle of programmatically moving the +// scrollbar. +// NOTE: As opposed to other places in the code, programmatically moving the +// scrollers from inside this class should not fire JS events. +- (BOOL)inProgramaticScroll; +@end diff --git a/Source/WebKit/mac/WebView/WebDynamicScrollBarsView.mm b/Source/WebKit/mac/WebView/WebDynamicScrollBarsView.mm new file mode 100644 index 0000000..79c3067 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDynamicScrollBarsView.mm @@ -0,0 +1,600 @@ +/* + * Copyright (C) 2005, 2008, 2010 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "WebDynamicScrollBarsViewInternal.h" + +#import "WebDocument.h" +#import "WebFrameInternal.h" +#import "WebFrameView.h" +#import "WebHTMLViewInternal.h" +#import <WebCore/Frame.h> +#import <WebCore/FrameView.h> +#import <WebKitSystemInterface.h> + +using namespace WebCore; + +// FIXME: <rdar://problem/5898985> Mail expects a constant of this name to exist. +const int WebCoreScrollbarAlwaysOn = ScrollbarAlwaysOn; + +#ifndef __OBJC2__ +// In <rdar://problem/7814899> we saw crashes because WebDynamicScrollBarsView increased in size, breaking ABI compatiblity. +COMPILE_ASSERT(sizeof(WebDynamicScrollBarsView) == 0x8c, WebDynamicScrollBarsView_is_expected_size); +#endif + +struct WebDynamicScrollBarsViewPrivate { + unsigned inUpdateScrollersLayoutPass; + + WebCore::ScrollbarMode hScroll; + WebCore::ScrollbarMode vScroll; + + bool hScrollModeLocked; + bool vScrollModeLocked; + bool suppressLayout; + bool suppressScrollers; + bool inUpdateScrollers; + bool verticallyPinnedByPreviousWheelEvent; + bool horizontallyPinnedByPreviousWheelEvent; + + bool allowsScrollersToOverlapContent; + bool alwaysHideHorizontalScroller; + bool alwaysHideVerticalScroller; + bool horizontalScrollingAllowedButScrollerHidden; + bool verticalScrollingAllowedButScrollerHidden; + + // scrollOrigin is set for various combinations of writing mode and direction. + // See the comment next to the corresponding member in ScrollView.h. + NSPoint scrollOrigin; + + // Flag to indicate that the scrollbar thumb's initial position needs to + // be manually set. + bool scrollOriginChanged; + NSPoint scrollPositionExcludingOrigin; + + bool inProgrammaticScroll; +}; + +@implementation WebDynamicScrollBarsView + +- (id)initWithFrame:(NSRect)frame +{ + if (!(self = [super initWithFrame:frame])) + return nil; + + _private = new WebDynamicScrollBarsViewPrivate; + memset(_private, 0, sizeof(WebDynamicScrollBarsViewPrivate)); + return self; +} + +- (id)initWithCoder:(NSCoder *)aDecoder +{ + if (!(self = [super initWithCoder:aDecoder])) + return nil; + + _private = new WebDynamicScrollBarsViewPrivate; + memset(_private, 0, sizeof(WebDynamicScrollBarsViewPrivate)); + return self; +} + +- (void)dealloc +{ + delete _private; + [super dealloc]; +} + +- (void)finalize +{ + delete _private; + [super finalize]; +} + +- (void)setAllowsHorizontalScrolling:(BOOL)flag +{ + if (_private->hScrollModeLocked) + return; + if (flag && _private->hScroll == ScrollbarAlwaysOff) + _private->hScroll = ScrollbarAuto; + else if (!flag && _private->hScroll != ScrollbarAlwaysOff) + _private->hScroll = ScrollbarAlwaysOff; + [self updateScrollers]; +} + +- (void)setAllowsScrollersToOverlapContent:(BOOL)flag +{ + if (_private->allowsScrollersToOverlapContent == flag) + return; + + _private->allowsScrollersToOverlapContent = flag; + + [[self contentView] setFrame:[self contentViewFrame]]; + [[self documentView] setNeedsLayout:YES]; + [[self documentView] layout]; +} + +- (void)setAlwaysHideHorizontalScroller:(BOOL)shouldBeHidden +{ + if (_private->alwaysHideHorizontalScroller == shouldBeHidden) + return; + + _private->alwaysHideHorizontalScroller = shouldBeHidden; + [self updateScrollers]; +} + +- (void)setAlwaysHideVerticalScroller:(BOOL)shouldBeHidden +{ + if (_private->alwaysHideVerticalScroller == shouldBeHidden) + return; + + _private->alwaysHideVerticalScroller = shouldBeHidden; + [self updateScrollers]; +} + +- (BOOL)horizontalScrollingAllowed +{ + return _private->horizontalScrollingAllowedButScrollerHidden || [self hasHorizontalScroller]; +} + +- (BOOL)verticalScrollingAllowed +{ + return _private->verticalScrollingAllowedButScrollerHidden || [self hasVerticalScroller]; +} + +- (BOOL)inProgramaticScroll +{ + return _private->inProgrammaticScroll; +} + +@end + +@implementation WebDynamicScrollBarsView (WebInternal) + +- (NSRect)contentViewFrame +{ + NSRect frame = [[self contentView] frame]; + + if ([self hasHorizontalScroller]) + frame.size.height = (_private->allowsScrollersToOverlapContent ? NSMaxY([[self horizontalScroller] frame]) : NSMinY([[self horizontalScroller] frame])); + if ([self hasVerticalScroller]) + frame.size.width = (_private->allowsScrollersToOverlapContent ? NSMaxX([[self verticalScroller] frame]) : NSMinX([[self verticalScroller] frame])); + return frame; +} + +- (void)tile +{ + [super tile]; + + // [super tile] sets the contentView size so that it does not overlap with the scrollers, + // we want to re-set the contentView to overlap scrollers before displaying. + if (_private->allowsScrollersToOverlapContent) + [[self contentView] setFrame:[self contentViewFrame]]; +} + +- (void)setSuppressLayout:(BOOL)flag +{ + _private->suppressLayout = flag; +} + +- (void)setScrollBarsSuppressed:(BOOL)suppressed repaintOnUnsuppress:(BOOL)repaint +{ + _private->suppressScrollers = suppressed; + + // This code was originally changes for a Leopard performance imporvement. We decided to + // ifdef it to fix correctness issues on Tiger documented in <rdar://problem/5441823>. +#ifndef BUILDING_ON_TIGER + if (suppressed) { + [[self verticalScroller] setNeedsDisplay:NO]; + [[self horizontalScroller] setNeedsDisplay:NO]; + } + + if (!suppressed && repaint) + [super reflectScrolledClipView:[self contentView]]; +#else + if (suppressed || repaint) { + [[self verticalScroller] setNeedsDisplay:!suppressed]; + [[self horizontalScroller] setNeedsDisplay:!suppressed]; + } +#endif +} + +- (void)adjustForScrollOriginChange +{ + if (!_private->scrollOriginChanged) + return; + + _private->scrollOriginChanged = false; + + NSView *documentView = [self documentView]; + NSRect documentRect = [documentView bounds]; + + // The call to [NSView scrollPoint:] fires off notification the handler for which needs to know that + // we're setting the initial scroll position so it doesn't interpret this as a user action and + // fire off a JS event. + _private->inProgrammaticScroll = true; + [documentView scrollPoint:NSMakePoint(_private->scrollPositionExcludingOrigin.x + documentRect.origin.x, _private->scrollPositionExcludingOrigin.y + documentRect.origin.y)]; + _private->inProgrammaticScroll = false; +} + +static const unsigned cMaxUpdateScrollbarsPass = 2; + +- (void)updateScrollers +{ + NSView *documentView = [self documentView]; + + // If we came in here with the view already needing a layout, then go ahead and do that + // first. (This will be the common case, e.g., when the page changes due to window resizing for example). + // This layout will not re-enter updateScrollers and does not count towards our max layout pass total. + if (!_private->suppressLayout && !_private->suppressScrollers && [documentView isKindOfClass:[WebHTMLView class]]) { + WebHTMLView* htmlView = (WebHTMLView*)documentView; + if ([htmlView _needsLayout]) { + _private->inUpdateScrollers = YES; + [(id <WebDocumentView>)documentView layout]; + _private->inUpdateScrollers = NO; + } + } + + BOOL hasHorizontalScroller = [self hasHorizontalScroller]; + BOOL hasVerticalScroller = [self hasVerticalScroller]; + + BOOL newHasHorizontalScroller = hasHorizontalScroller; + BOOL newHasVerticalScroller = hasVerticalScroller; + + if (!documentView) { + newHasHorizontalScroller = NO; + newHasVerticalScroller = NO; + } + + if (_private->hScroll != ScrollbarAuto) + newHasHorizontalScroller = (_private->hScroll == ScrollbarAlwaysOn); + if (_private->vScroll != ScrollbarAuto) + newHasVerticalScroller = (_private->vScroll == ScrollbarAlwaysOn); + + if (!documentView || _private->suppressLayout || _private->suppressScrollers || (_private->hScroll != ScrollbarAuto && _private->vScroll != ScrollbarAuto)) { + _private->horizontalScrollingAllowedButScrollerHidden = newHasHorizontalScroller && _private->alwaysHideHorizontalScroller; + if (_private->horizontalScrollingAllowedButScrollerHidden) + newHasHorizontalScroller = NO; + + _private->verticalScrollingAllowedButScrollerHidden = newHasVerticalScroller && _private->alwaysHideVerticalScroller; + if (_private->verticalScrollingAllowedButScrollerHidden) + newHasVerticalScroller = NO; + + _private->inUpdateScrollers = YES; + if (hasHorizontalScroller != newHasHorizontalScroller) + [self setHasHorizontalScroller:newHasHorizontalScroller]; + if (hasVerticalScroller != newHasVerticalScroller) + [self setHasVerticalScroller:newHasVerticalScroller]; + if (_private->suppressScrollers) { + [[self verticalScroller] setNeedsDisplay:NO]; + [[self horizontalScroller] setNeedsDisplay:NO]; + } + _private->inUpdateScrollers = NO; + return; + } + + BOOL needsLayout = NO; + + NSSize documentSize = [documentView frame].size; + NSSize visibleSize = [self documentVisibleRect].size; + NSSize frameSize = [self frame].size; + + // When in HiDPI with a scale factor > 1, the visibleSize and frameSize may be non-integral values, + // while the documentSize (set by WebCore) will be integral. Round up the non-integral sizes so that + // the mismatch won't cause unwanted scrollbars to appear. This can result in slightly cut off content, + // but it will always be less than one pixel, which should not be noticeable. + visibleSize.width = ceilf(visibleSize.width); + visibleSize.height = ceilf(visibleSize.height); + frameSize.width = ceilf(frameSize.width); + frameSize.height = ceilf(frameSize.height); + + if (_private->hScroll == ScrollbarAuto) { + newHasHorizontalScroller = documentSize.width > visibleSize.width; + if (newHasHorizontalScroller && !_private->inUpdateScrollersLayoutPass && documentSize.height <= frameSize.height && documentSize.width <= frameSize.width) + newHasHorizontalScroller = NO; + } + + if (_private->vScroll == ScrollbarAuto) { + newHasVerticalScroller = documentSize.height > visibleSize.height; + if (newHasVerticalScroller && !_private->inUpdateScrollersLayoutPass && documentSize.height <= frameSize.height && documentSize.width <= frameSize.width) + newHasVerticalScroller = NO; + } + + // Unless in ScrollbarsAlwaysOn mode, if we ever turn one scrollbar off, always turn the other one off too. + // Never ever try to both gain/lose a scrollbar in the same pass. + if (!newHasHorizontalScroller && hasHorizontalScroller && _private->vScroll != ScrollbarAlwaysOn) + newHasVerticalScroller = NO; + if (!newHasVerticalScroller && hasVerticalScroller && _private->hScroll != ScrollbarAlwaysOn) + newHasHorizontalScroller = NO; + + _private->horizontalScrollingAllowedButScrollerHidden = newHasHorizontalScroller && _private->alwaysHideHorizontalScroller; + if (_private->horizontalScrollingAllowedButScrollerHidden) + newHasHorizontalScroller = NO; + + _private->verticalScrollingAllowedButScrollerHidden = newHasVerticalScroller && _private->alwaysHideVerticalScroller; + if (_private->verticalScrollingAllowedButScrollerHidden) + newHasVerticalScroller = NO; + + if (hasHorizontalScroller != newHasHorizontalScroller) { + _private->inUpdateScrollers = YES; + [self setHasHorizontalScroller:newHasHorizontalScroller]; + _private->inUpdateScrollers = NO; + needsLayout = YES; + NSView *documentView = [self documentView]; + NSRect documentRect = [documentView bounds]; + if (documentRect.origin.y < 0 && !newHasHorizontalScroller) + [documentView setBoundsOrigin:NSMakePoint(documentRect.origin.x, documentRect.origin.y + 15)]; + } + + if (hasVerticalScroller != newHasVerticalScroller) { + _private->inUpdateScrollers = YES; + [self setHasVerticalScroller:newHasVerticalScroller]; + _private->inUpdateScrollers = NO; + needsLayout = YES; + NSView *documentView = [self documentView]; + NSRect documentRect = [documentView bounds]; + if (documentRect.origin.x < 0 && !newHasVerticalScroller) + [documentView setBoundsOrigin:NSMakePoint(documentRect.origin.x + 15, documentRect.origin.y)]; + } + + if (needsLayout && _private->inUpdateScrollersLayoutPass < cMaxUpdateScrollbarsPass && + [documentView conformsToProtocol:@protocol(WebDocumentView)]) { + _private->inUpdateScrollersLayoutPass++; + [(id <WebDocumentView>)documentView setNeedsLayout:YES]; + [(id <WebDocumentView>)documentView layout]; + NSSize newDocumentSize = [documentView frame].size; + if (NSEqualSizes(documentSize, newDocumentSize)) { + // The layout with the new scroll state had no impact on + // the document's overall size, so updateScrollers didn't get called. + // Recur manually. + [self updateScrollers]; + } + _private->inUpdateScrollersLayoutPass--; + } +} + +// Make the horizontal and vertical scroll bars come and go as needed. +- (void)reflectScrolledClipView:(NSClipView *)clipView +{ + if (clipView == [self contentView]) { + // Prevent appearance of trails because of overlapping views + if (_private->allowsScrollersToOverlapContent) + [self setDrawsBackground:NO]; + + // FIXME: This hack here prevents infinite recursion that takes place when we + // gyrate between having a vertical scroller and not having one. A reproducible + // case is clicking on the "the Policy Routing text" link at + // http://www.linuxpowered.com/archive/howto/Net-HOWTO-8.html. + // The underlying cause is some problem in the NSText machinery, but I was not + // able to pin it down. + NSGraphicsContext *currentContext = [NSGraphicsContext currentContext]; + if (!_private->inUpdateScrollers && (!currentContext || [currentContext isDrawingToScreen])) + [self updateScrollers]; + } + + // This code was originally changed for a Leopard performance imporvement. We decided to + // ifdef it to fix correctness issues on Tiger documented in <rdar://problem/5441823>. +#ifndef BUILDING_ON_TIGER + // Update the scrollers if they're not being suppressed. + if (!_private->suppressScrollers) + [super reflectScrolledClipView:clipView]; +#else + [super reflectScrolledClipView:clipView]; + + // Validate the scrollers if they're being suppressed. + if (_private->suppressScrollers) { + [[self verticalScroller] setNeedsDisplay:NO]; + [[self horizontalScroller] setNeedsDisplay:NO]; + } +#endif + + // The call to [NSView reflectScrolledClipView] sets the scrollbar thumb + // position to 0 (the left) when the view is initially displayed. + // This call updates the initial position correctly. + [self adjustForScrollOriginChange]; + +#if USE(ACCELERATED_COMPOSITING) && defined(BUILDING_ON_LEOPARD) + NSView *documentView = [self documentView]; + if ([documentView isKindOfClass:[WebHTMLView class]]) { + WebHTMLView *htmlView = (WebHTMLView *)documentView; + if ([htmlView _isUsingAcceleratedCompositing]) + [htmlView _updateLayerHostingViewPosition]; + } +#endif +} + +- (BOOL)allowsHorizontalScrolling +{ + return _private->hScroll != ScrollbarAlwaysOff; +} + +- (BOOL)allowsVerticalScrolling +{ + return _private->vScroll != ScrollbarAlwaysOff; +} + +- (void)scrollingModes:(WebCore::ScrollbarMode*)hMode vertical:(WebCore::ScrollbarMode*)vMode +{ + *hMode = _private->hScroll; + *vMode = _private->vScroll; +} + +- (ScrollbarMode)horizontalScrollingMode +{ + return _private->hScroll; +} + +- (ScrollbarMode)verticalScrollingMode +{ + return _private->vScroll; +} + +- (void)setHorizontalScrollingMode:(ScrollbarMode)horizontalMode andLock:(BOOL)lock +{ + [self setScrollingModes:horizontalMode vertical:[self verticalScrollingMode] andLock:lock]; +} + +- (void)setVerticalScrollingMode:(ScrollbarMode)verticalMode andLock:(BOOL)lock +{ + [self setScrollingModes:[self horizontalScrollingMode] vertical:verticalMode andLock:lock]; +} + +// Mail uses this method, so we cannot remove it. +- (void)setVerticalScrollingMode:(ScrollbarMode)verticalMode +{ + [self setScrollingModes:[self horizontalScrollingMode] vertical:verticalMode andLock:NO]; +} + +- (void)setScrollingModes:(ScrollbarMode)horizontalMode vertical:(ScrollbarMode)verticalMode andLock:(BOOL)lock +{ + BOOL update = NO; + if (verticalMode != _private->vScroll && !_private->vScrollModeLocked) { + _private->vScroll = verticalMode; + update = YES; + } + + if (horizontalMode != _private->hScroll && !_private->hScrollModeLocked) { + _private->hScroll = horizontalMode; + update = YES; + } + + if (lock) + [self setScrollingModesLocked:YES]; + + if (update) + [self updateScrollers]; +} + +- (void)setHorizontalScrollingModeLocked:(BOOL)locked +{ + _private->hScrollModeLocked = locked; +} + +- (void)setVerticalScrollingModeLocked:(BOOL)locked +{ + _private->vScrollModeLocked = locked; +} + +- (void)setScrollingModesLocked:(BOOL)locked +{ + _private->hScrollModeLocked = _private->vScrollModeLocked = locked; +} + +- (BOOL)horizontalScrollingModeLocked +{ + return _private->hScrollModeLocked; +} + +- (BOOL)verticalScrollingModeLocked +{ + return _private->vScrollModeLocked; +} + +- (BOOL)autoforwardsScrollWheelEvents +{ + return YES; +} + +- (void)scrollWheel:(NSEvent *)event +{ + float deltaX; + float deltaY; + BOOL isContinuous; + WKGetWheelEventDeltas(event, &deltaX, &deltaY, &isContinuous); + + BOOL isLatchingEvent = WKIsLatchingWheelEvent(event); + + if (fabsf(deltaY) > fabsf(deltaX)) { + if (![self allowsVerticalScrolling]) { + [[self nextResponder] scrollWheel:event]; + return; + } + + if (isLatchingEvent && !_private->verticallyPinnedByPreviousWheelEvent) { + double verticalPosition = [[self verticalScroller] doubleValue]; + if ((deltaY >= 0.0 && verticalPosition == 0.0) || (deltaY <= 0.0 && verticalPosition == 1.0)) + return; + } + } else { + if (![self allowsHorizontalScrolling]) { + [[self nextResponder] scrollWheel:event]; + return; + } + + if (isLatchingEvent && !_private->horizontallyPinnedByPreviousWheelEvent) { + double horizontalPosition = [[self horizontalScroller] doubleValue]; + if ((deltaX >= 0.0 && horizontalPosition == 0.0) || (deltaX <= 0.0 && horizontalPosition == 1.0)) + return; + } + } + + // Calling super can release the last reference. <rdar://problem/7400263> + // Hold a reference so the code following the super call will not crash. + [self retain]; + + [super scrollWheel:event]; + + if (!isLatchingEvent) { + double verticalPosition = [[self verticalScroller] doubleValue]; + double horizontalPosition = [[self horizontalScroller] doubleValue]; + + _private->verticallyPinnedByPreviousWheelEvent = (verticalPosition == 0.0 || verticalPosition == 1.0); + _private->horizontallyPinnedByPreviousWheelEvent = (horizontalPosition == 0.0 || horizontalPosition == 1.0); + } + + [self release]; +} + +- (BOOL)accessibilityIsIgnored +{ + id docView = [self documentView]; + if ([docView isKindOfClass:[WebFrameView class]] && ![(WebFrameView *)docView allowsScrolling]) + return YES; + + return [super accessibilityIsIgnored]; +} + +- (void)setScrollOrigin:(NSPoint)scrollOrigin updatePosition:(BOOL)updatePosition +{ + // The cross-platform ScrollView call already checked to see if the old/new scroll origins were the same or not + // so we don't have to check for equivalence here. + _private->scrollOrigin = scrollOrigin; + id docView = [self documentView]; + + NSRect visibleRect = [self documentVisibleRect]; + + [docView setBoundsOrigin:NSMakePoint(-scrollOrigin.x, -scrollOrigin.y)]; + + _private->scrollOriginChanged = true; + + // Maintain our original position in the presence of the new scroll origin. + _private->scrollPositionExcludingOrigin = NSMakePoint(visibleRect.origin.x + scrollOrigin.x, visibleRect.origin.y + scrollOrigin.y); + + if (updatePosition) // Otherwise we'll just let the snap happen when we update for the resize. + [self adjustForScrollOriginChange]; +} + +- (NSPoint)scrollOrigin +{ + return _private->scrollOrigin; +} + +@end diff --git a/Source/WebKit/mac/WebView/WebDynamicScrollBarsViewInternal.h b/Source/WebKit/mac/WebView/WebDynamicScrollBarsViewInternal.h new file mode 100644 index 0000000..40be88d --- /dev/null +++ b/Source/WebKit/mac/WebView/WebDynamicScrollBarsViewInternal.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005, 2008 Apple Inc. All rights reserved. + * + * 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 "WebDynamicScrollBarsView.h" +#import <WebCore/WebCoreFrameView.h> + +@interface WebDynamicScrollBarsView (WebInternal) <WebCoreFrameScrollView> + +- (BOOL)allowsHorizontalScrolling; +- (BOOL)allowsVerticalScrolling; + +- (void)setScrollingModes:(WebCore::ScrollbarMode)hMode vertical:(WebCore::ScrollbarMode)vMode andLock:(BOOL)lock; +- (void)scrollingModes:(WebCore::ScrollbarMode*)hMode vertical:(WebCore::ScrollbarMode*)vMode; + +- (WebCore::ScrollbarMode)horizontalScrollingMode; +- (WebCore::ScrollbarMode)verticalScrollingMode; + +- (void)setHorizontalScrollingMode:(WebCore::ScrollbarMode)mode andLock:(BOOL)lock; +- (void)setVerticalScrollingMode:(WebCore::ScrollbarMode)mode andLock:(BOOL)lock; + +- (void)setHorizontalScrollingModeLocked:(BOOL)locked; +- (void)setVerticalScrollingModeLocked:(BOOL)locked; +- (void)setScrollingModesLocked:(BOOL)mode; + +- (BOOL)horizontalScrollingModeLocked; +- (BOOL)verticalScrollingModeLocked; + +- (void)updateScrollers; +- (void)setSuppressLayout:(BOOL)flag; + +// Calculate the appropriate frame for the contentView based on allowsScrollersToOverlapContent. +- (NSRect)contentViewFrame; +@end diff --git a/Source/WebKit/mac/WebView/WebEditingDelegate.h b/Source/WebKit/mac/WebView/WebEditingDelegate.h new file mode 100644 index 0000000..5de9ef0 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebEditingDelegate.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2004, 2005 Apple Computer, Inc. All rights reserved. + * + * 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 <Cocoa/Cocoa.h> + +@class DOMCSSStyleDeclaration; +@class DOMRange; +@class WebView; + +typedef enum { + WebViewInsertActionTyped, + WebViewInsertActionPasted, + WebViewInsertActionDropped, +} WebViewInsertAction; + +@interface NSObject (WebViewEditingDelegate) +- (BOOL)webView:(WebView *)webView shouldBeginEditingInDOMRange:(DOMRange *)range; +- (BOOL)webView:(WebView *)webView shouldEndEditingInDOMRange:(DOMRange *)range; +- (BOOL)webView:(WebView *)webView shouldInsertNode:(DOMNode *)node replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action; +- (BOOL)webView:(WebView *)webView shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action; +- (BOOL)webView:(WebView *)webView shouldDeleteDOMRange:(DOMRange *)range; +- (BOOL)webView:(WebView *)webView shouldChangeSelectedDOMRange:(DOMRange *)currentRange toDOMRange:(DOMRange *)proposedRange affinity:(NSSelectionAffinity)selectionAffinity stillSelecting:(BOOL)flag; +- (BOOL)webView:(WebView *)webView shouldApplyStyle:(DOMCSSStyleDeclaration *)style toElementsInDOMRange:(DOMRange *)range; +- (BOOL)webView:(WebView *)webView shouldChangeTypingStyle:(DOMCSSStyleDeclaration *)currentStyle toStyle:(DOMCSSStyleDeclaration *)proposedStyle; +- (BOOL)webView:(WebView *)webView doCommandBySelector:(SEL)selector; +- (void)webViewDidBeginEditing:(NSNotification *)notification; +- (void)webViewDidChange:(NSNotification *)notification; +- (void)webViewDidEndEditing:(NSNotification *)notification; +- (void)webViewDidChangeTypingStyle:(NSNotification *)notification; +- (void)webViewDidChangeSelection:(NSNotification *)notification; +- (NSUndoManager *)undoManagerForWebView:(WebView *)webView; +@end diff --git a/Source/WebKit/mac/WebView/WebEditingDelegatePrivate.h b/Source/WebKit/mac/WebView/WebEditingDelegatePrivate.h new file mode 100644 index 0000000..1116de3 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebEditingDelegatePrivate.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * 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 <WebKit/WebEditingDelegate.h> + +@class DOMHTMLElement; + +@interface NSObject (WebViewEditingDelegatePrivate) +- (BOOL)webView:(WebView *)webView shouldShowDeleteInterfaceForElement:(DOMHTMLElement *)element; +- (void)webView:(WebView *)webView didWriteSelectionToPasteboard:(NSPasteboard *)pasteboard; +- (void)webView:(WebView *)webView didSetSelectionTypesForPasteboard:(NSPasteboard *)pasteboard; +- (BOOL)webView:(WebView *)webView shouldMoveRangeAfterDelete:(DOMRange *)range replacingRange:(DOMRange *)rangeToBeReplaced; +@end diff --git a/Source/WebKit/mac/WebView/WebFormDelegate.h b/Source/WebKit/mac/WebView/WebFormDelegate.h new file mode 100644 index 0000000..1f4bee0 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebFormDelegate.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2003, 2005, 2006 Apple Computer, Inc. All rights reserved. + * + * 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 <AppKit/AppKit.h> + +@class DOMElement; +@class DOMHTMLInputElement; +@class DOMHTMLTextAreaElement; +@class WebFrame; + +/*! + @protocol WebFormSubmissionListener +*/ +@protocol WebFormSubmissionListener <NSObject> +- (void)continue; +@end + +/*! + @protocol WebFormDelegate +*/ +@protocol WebFormDelegate <NSObject> + +// Various methods send by controls that edit text to their delegates, which are all +// analogous to similar methods in AppKit/NSControl.h. +// These methods are forwarded from widgets used in forms to the WebFormDelegate. + +- (void)textFieldDidBeginEditing:(DOMHTMLInputElement *)element inFrame:(WebFrame *)frame; +- (void)textFieldDidEndEditing:(DOMHTMLInputElement *)element inFrame:(WebFrame *)frame; +- (void)textDidChangeInTextField:(DOMHTMLInputElement *)element inFrame:(WebFrame *)frame; +- (void)textDidChangeInTextArea:(DOMHTMLTextAreaElement *)element inFrame:(WebFrame *)frame; + +- (BOOL)textField:(DOMHTMLInputElement *)element doCommandBySelector:(SEL)commandSelector inFrame:(WebFrame *)frame; +- (BOOL)textField:(DOMHTMLInputElement *)element shouldHandleEvent:(NSEvent *)event inFrame:(WebFrame *)frame; + +// Sent when a form is just about to be submitted (before the load is started) +// listener must be sent continue when the delegate is done. +- (void)frame:(WebFrame *)frame sourceFrame:(WebFrame *)sourceFrame willSubmitForm:(DOMElement *)form + withValues:(NSDictionary *)values submissionListener:(id <WebFormSubmissionListener>)listener; + +@end + +/*! + @class WebFormDelegate + @discussion The WebFormDelegate class responds to all WebFormDelegate protocol + methods by doing nothing. It's provided for the convenience of clients who only want + to implement some of the above methods and ignore others. +*/ +@interface WebFormDelegate : NSObject <WebFormDelegate> +@end diff --git a/Source/WebKit/mac/WebView/WebFormDelegate.m b/Source/WebKit/mac/WebView/WebFormDelegate.m new file mode 100644 index 0000000..df25f20 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebFormDelegate.m @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. + * + * 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 "WebFormDelegatePrivate.h" + +// FIXME: This could become an informal protocol; we switched all the API +// delegates to be informal. + +@implementation WebFormDelegate + +static WebFormDelegate *sharedDelegate = nil; + +// Return a object with NOP implementations of the protocol's methods +// Note this feature relies on our default delegate being stateless ++ (WebFormDelegate *)_sharedWebFormDelegate +{ + if (!sharedDelegate) + sharedDelegate = [[WebFormDelegate alloc] init]; + return sharedDelegate; +} + +- (void)textFieldDidBeginEditing:(DOMHTMLInputElement *)element inFrame:(WebFrame *)frame +{ +} + +- (void)textFieldDidEndEditing:(DOMHTMLInputElement *)element inFrame:(WebFrame *)frame +{ +} + +- (void)textDidChangeInTextField:(DOMHTMLInputElement *)element inFrame:(WebFrame *)frame +{ +} + +- (void)textDidChangeInTextArea:(DOMHTMLTextAreaElement *)element inFrame:(WebFrame *)frame +{ +} + +- (BOOL)textField:(DOMHTMLInputElement *)element doCommandBySelector:(SEL)commandSelector inFrame:(WebFrame *)frame +{ + return NO; +} + +- (BOOL)textField:(DOMHTMLInputElement *)element shouldHandleEvent:(NSEvent *)event inFrame:(WebFrame *)frame +{ + return NO; +} + +- (void)frame:(WebFrame *)frame sourceFrame:(WebFrame *)sourceFrame willSubmitForm:(DOMElement *)form + withValues:(NSDictionary *)values submissionListener:(id <WebFormSubmissionListener>)listener +{ + [listener continue]; +} + +@end diff --git a/Source/WebKit/mac/WebView/WebFormDelegatePrivate.h b/Source/WebKit/mac/WebView/WebFormDelegatePrivate.h new file mode 100644 index 0000000..d566e89 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebFormDelegatePrivate.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * 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 "WebFormDelegate.h" + +@interface WebFormDelegate (WebPrivate) ++ (WebFormDelegate *)_sharedWebFormDelegate; +@end diff --git a/Source/WebKit/mac/WebView/WebFrame.h b/Source/WebKit/mac/WebView/WebFrame.h new file mode 100644 index 0000000..64015fd --- /dev/null +++ b/Source/WebKit/mac/WebView/WebFrame.h @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2007 Apple Inc. All rights reserved. + * + * 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 <Foundation/Foundation.h> +#import <JavaScriptCore/JSBase.h> + +@class DOMDocument; +@class DOMHTMLElement; +@class NSURLRequest; +@class WebArchive; +@class WebDataSource; +@class WebFramePrivate; +@class WebFrameView; +@class WebScriptObject; +@class WebView; + +/*! + @class WebFrame + @discussion Every web page is represented by at least one WebFrame. A WebFrame + has a WebFrameView and a WebDataSource. +*/ +@interface WebFrame : NSObject +{ +@private + WebFramePrivate *_private; +} + +/*! + @method initWithName:webFrameView:webView: + @abstract The designated initializer of WebFrame. + @discussion WebFrames are normally created for you by the WebView. You should + not need to invoke this method directly. + @param name The name of the frame. + @param view The WebFrameView for the frame. + @param webView The WebView that manages the frame. + @result Returns an initialized WebFrame. +*/ +- (id)initWithName:(NSString *)name webFrameView:(WebFrameView *)view webView:(WebView *)webView; + +/*! + @method name + @result The frame name. +*/ +- (NSString *)name; + +/*! + @method webView + @result Returns the WebView for the document that includes this frame. +*/ +- (WebView *)webView; + +/*! + @method frameView + @result The WebFrameView for this frame. +*/ +- (WebFrameView *)frameView; + +/*! + @method DOMDocument + @abstract Returns the DOM document of the frame. + @description Returns nil if the frame does not contain a DOM document such as a standalone image. +*/ +- (DOMDocument *)DOMDocument; + +/*! + @method frameElement + @abstract Returns the frame element of the frame. + @description The class of the result is either DOMHTMLFrameElement, DOMHTMLIFrameElement or DOMHTMLObjectElement. + Returns nil if the frame is the main frame since there is no frame element for the frame in this case. +*/ +- (DOMHTMLElement *)frameElement; + +/*! + @method loadRequest: + @param request The web request to load. +*/ +- (void)loadRequest:(NSURLRequest *)request; + +/*! + @method loadData:MIMEType:textEncodingName:baseURL: + @param data The data to use for the main page of the document. + @param MIMEType The MIME type of the data. + @param encodingName The encoding of the data. + @param URL The base URL to apply to relative URLs within the document. +*/ +- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)URL; + +/*! + @method loadHTMLString:baseURL: + @param string The string to use for the main page of the document. + @param URL The base URL to apply to relative URLs within the document. +*/ +- (void)loadHTMLString:(NSString *)string baseURL:(NSURL *)URL; + +/*! + @method loadAlternateHTMLString:baseURL:forUnreachableURL: + @abstract Loads a page to display as a substitute for a URL that could not be reached. + @discussion This allows clients to display page-loading errors in the webview itself. + This is typically called while processing the WebFrameLoadDelegate method + -webView:didFailProvisionalLoadWithError:forFrame: or one of the the WebPolicyDelegate methods + -webView:decidePolicyForMIMEType:request:frame:decisionListener: or + -webView:unableToImplementPolicyWithError:frame:. If it is called from within one of those + three delegate methods then the back/forward list will be maintained appropriately. + @param string The string to use for the main page of the document. + @param baseURL The baseURL to apply to relative URLs within the document. + @param unreachableURL The URL for which this page will serve as alternate content. +*/ +- (void)loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)baseURL forUnreachableURL:(NSURL *)unreachableURL; + +/*! + @method loadArchive: + @abstract Causes WebFrame to load a WebArchive. + @param archive The archive to be loaded. +*/ +- (void)loadArchive:(WebArchive *)archive; + +/*! + @method dataSource + @discussion Returns the committed data source. Will return nil if the + provisional data source hasn't yet been loaded. + @result The datasource for this frame. +*/ +- (WebDataSource *)dataSource; + +/*! + @method provisionalDataSource + @discussion Will return the provisional data source. The provisional data source will + be nil if no data source has been set on the frame, or the data source + has successfully transitioned to the committed data source. + @result The provisional datasource of this frame. +*/ +- (WebDataSource *)provisionalDataSource; + +/*! + @method stopLoading + @discussion Stop any pending loads on the frame's data source, + and its children. +*/ +- (void)stopLoading; + +/*! + @method reload + @discussion Performs HTTP/1.1 end-to-end revalidation using cache-validating conditionals if possible. +*/ +- (void)reload; + +/*! + @method reloadFromOrigin + @discussion Performs HTTP/1.1 end-to-end reload. +*/ +- (void)reloadFromOrigin; + +/*! + @method findFrameNamed: + @discussion This method returns a frame with the given name. findFrameNamed returns self + for _self and _current, the parent frame for _parent and the main frame for _top. + findFrameNamed returns self for _parent and _top if the receiver is the mainFrame. + findFrameNamed first searches from the current frame to all descending frames then the + rest of the frames in the WebView. If still not found, findFrameNamed searches the + frames of the other WebViews. + @param name The name of the frame to find. + @result The frame matching the provided name. nil if the frame is not found. +*/ +- (WebFrame *)findFrameNamed:(NSString *)name; + +/*! + @method parentFrame + @result The frame containing this frame, or nil if this is a top level frame. +*/ +- (WebFrame *)parentFrame; + +/*! + @method childFrames + @discussion The frames in the array are associated with a frame set or iframe. + @result Returns an array of WebFrame. +*/ +- (NSArray *)childFrames; + +/*! + @method windowObject + @result The WebScriptObject representing the frame's JavaScript window object. +*/ +- (WebScriptObject *)windowObject; + +/*! + @method globalContext + @result The frame's global JavaScript execution context. Use this method to + bridge between the WebKit and JavaScriptCore APIs. +*/ +- (JSGlobalContextRef)globalContext; +@end diff --git a/Source/WebKit/mac/WebView/WebFrame.mm b/Source/WebKit/mac/WebView/WebFrame.mm new file mode 100644 index 0000000..3dde06f --- /dev/null +++ b/Source/WebKit/mac/WebView/WebFrame.mm @@ -0,0 +1,1614 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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 "WebFrameInternal.h" + +#import "DOMCSSStyleDeclarationInternal.h" +#import "DOMDocumentFragmentInternal.h" +#import "DOMDocumentInternal.h" +#import "DOMElementInternal.h" +#import "DOMHTMLElementInternal.h" +#import "DOMNodeInternal.h" +#import "DOMRangeInternal.h" +#import "WebArchiveInternal.h" +#import "WebChromeClient.h" +#import "WebDataSourceInternal.h" +#import "WebDocumentLoaderMac.h" +#import "WebDynamicScrollBarsView.h" +#import "WebFrameLoaderClient.h" +#import "WebFrameViewInternal.h" +#import "WebHTMLView.h" +#import "WebHTMLViewInternal.h" +#import "WebKitStatisticsPrivate.h" +#import "WebKitVersionChecks.h" +#import "WebNSObjectExtras.h" +#import "WebNSURLExtras.h" +#import "WebScriptDebugger.h" +#import "WebScriptWorldInternal.h" +#import "WebViewInternal.h" +#import <JavaScriptCore/APICast.h> +#import <WebCore/AXObjectCache.h> +#import <WebCore/AccessibilityObject.h> +#import <WebCore/AnimationController.h> +#import <WebCore/CSSMutableStyleDeclaration.h> +#import <WebCore/CachedResourceLoader.h> +#import <WebCore/Chrome.h> +#import <WebCore/ColorMac.h> +#import <WebCore/DOMImplementation.h> +#import <WebCore/DocumentFragment.h> +#import <WebCore/DocumentMarkerController.h> +#import <WebCore/EventHandler.h> +#import <WebCore/EventNames.h> +#import <WebCore/Frame.h> +#import <WebCore/FrameLoader.h> +#import <WebCore/FrameLoaderStateMachine.h> +#import <WebCore/FrameTree.h> +#import <WebCore/GraphicsContext.h> +#import <WebCore/HTMLFrameOwnerElement.h> +#import <WebCore/HistoryItem.h> +#import <WebCore/HitTestResult.h> +#import <WebCore/LegacyWebArchive.h> +#import <WebCore/Page.h> +#import <WebCore/PluginData.h> +#import <WebCore/PrintContext.h> +#import <WebCore/RenderLayer.h> +#import <WebCore/RenderPart.h> +#import <WebCore/RenderView.h> +#import <WebCore/ReplaceSelectionCommand.h> +#import <WebCore/RuntimeApplicationChecks.h> +#import <WebCore/ScriptValue.h> +#import <WebCore/SmartReplace.h> +#import <WebCore/SVGDocumentExtensions.h> +#import <WebCore/SVGSMILElement.h> +#import <WebCore/TextIterator.h> +#import <WebCore/ThreadCheck.h> +#import <WebCore/TypingCommand.h> +#import <WebCore/htmlediting.h> +#import <WebCore/markup.h> +#import <WebCore/visible_units.h> +#import <WebKitSystemInterface.h> +#import <runtime/JSLock.h> +#import <runtime/JSObject.h> +#import <runtime/JSValue.h> +#import <wtf/CurrentTime.h> + +using namespace std; +using namespace WebCore; +using namespace HTMLNames; + +using JSC::JSGlobalObject; +using JSC::JSLock; +using JSC::JSValue; +using JSC::SilenceAssertionsOnly; + +/* +Here is the current behavior matrix for four types of navigations: + +Standard Nav: + + Restore form state: YES + Restore scroll and focus state: YES + Cache policy: NSURLRequestUseProtocolCachePolicy + Add to back/forward list: YES + +Back/Forward: + + Restore form state: YES + Restore scroll and focus state: YES + Cache policy: NSURLRequestReturnCacheDataElseLoad + Add to back/forward list: NO + +Reload (meaning only the reload button): + + Restore form state: NO + Restore scroll and focus state: YES + Cache policy: NSURLRequestReloadIgnoringCacheData + Add to back/forward list: NO + +Repeat load of the same URL (by any other means of navigation other than the reload button, including hitting return in the location field): + + Restore form state: NO + Restore scroll and focus state: NO, reset to initial conditions + Cache policy: NSURLRequestReloadIgnoringCacheData + Add to back/forward list: NO +*/ + +NSString *WebPageCacheEntryDateKey = @"WebPageCacheEntryDateKey"; +NSString *WebPageCacheDataSourceKey = @"WebPageCacheDataSourceKey"; +NSString *WebPageCacheDocumentViewKey = @"WebPageCacheDocumentViewKey"; + +NSString *WebFrameMainDocumentError = @"WebFrameMainDocumentErrorKey"; +NSString *WebFrameHasPlugins = @"WebFrameHasPluginsKey"; +NSString *WebFrameHasUnloadListener = @"WebFrameHasUnloadListenerKey"; +NSString *WebFrameUsesDatabases = @"WebFrameUsesDatabasesKey"; +NSString *WebFrameUsesGeolocation = @"WebFrameUsesGeolocationKey"; +NSString *WebFrameUsesApplicationCache = @"WebFrameUsesApplicationCacheKey"; +NSString *WebFrameCanSuspendActiveDOMObjects = @"WebFrameCanSuspendActiveDOMObjectsKey"; + +// FIXME: Remove when this key becomes publicly defined +NSString *NSAccessibilityEnhancedUserInterfaceAttribute = @"AXEnhancedUserInterface"; + +@implementation WebFramePrivate + +- (void)dealloc +{ + [webFrameView release]; + + delete scriptDebugger; + + [super dealloc]; +} + +- (void)finalize +{ + delete scriptDebugger; + + [super finalize]; +} + +- (void)setWebFrameView:(WebFrameView *)v +{ + [v retain]; + [webFrameView release]; + webFrameView = v; +} + +@end + +EditableLinkBehavior core(WebKitEditableLinkBehavior editableLinkBehavior) +{ + switch (editableLinkBehavior) { + case WebKitEditableLinkDefaultBehavior: + return EditableLinkDefaultBehavior; + case WebKitEditableLinkAlwaysLive: + return EditableLinkAlwaysLive; + case WebKitEditableLinkOnlyLiveWithShiftKey: + return EditableLinkOnlyLiveWithShiftKey; + case WebKitEditableLinkLiveWhenNotFocused: + return EditableLinkLiveWhenNotFocused; + case WebKitEditableLinkNeverLive: + return EditableLinkNeverLive; + } + ASSERT_NOT_REACHED(); + return EditableLinkDefaultBehavior; +} + +WebCore::EditingBehaviorType core(WebKitEditingBehavior behavior) +{ + switch (behavior) { + case WebKitEditingMacBehavior: + return WebCore::EditingMacBehavior; + case WebKitEditingWinBehavior: + return WebCore::EditingWindowsBehavior; + case WebKitEditingUnixBehavior: + return WebCore::EditingUnixBehavior; + } + ASSERT_NOT_REACHED(); + return WebCore::EditingMacBehavior; +} + +TextDirectionSubmenuInclusionBehavior core(WebTextDirectionSubmenuInclusionBehavior behavior) +{ + switch (behavior) { + case WebTextDirectionSubmenuNeverIncluded: + return TextDirectionSubmenuNeverIncluded; + case WebTextDirectionSubmenuAutomaticallyIncluded: + return TextDirectionSubmenuAutomaticallyIncluded; + case WebTextDirectionSubmenuAlwaysIncluded: + return TextDirectionSubmenuAlwaysIncluded; + } + ASSERT_NOT_REACHED(); + return TextDirectionSubmenuNeverIncluded; +} + +@implementation WebFrame (WebInternal) + +Frame* core(WebFrame *frame) +{ + return frame ? frame->_private->coreFrame : 0; +} + +WebFrame *kit(Frame* frame) +{ + return frame ? static_cast<WebFrameLoaderClient*>(frame->loader()->client())->webFrame() : nil; +} + +Page* core(WebView *webView) +{ + return [webView page]; +} + +WebView *kit(Page* page) +{ + return page ? static_cast<WebChromeClient*>(page->chrome()->client())->webView() : nil; +} + +WebView *getWebView(WebFrame *webFrame) +{ + Frame* coreFrame = core(webFrame); + if (!coreFrame) + return nil; + return kit(coreFrame->page()); +} + ++ (PassRefPtr<Frame>)_createFrameWithPage:(Page*)page frameName:(const String&)name frameView:(WebFrameView *)frameView ownerElement:(HTMLFrameOwnerElement*)ownerElement +{ + WebView *webView = kit(page); + + WebFrame *frame = [[self alloc] _initWithWebFrameView:frameView webView:webView]; + RefPtr<Frame> coreFrame = Frame::create(page, ownerElement, new WebFrameLoaderClient(frame)); + [frame release]; + frame->_private->coreFrame = coreFrame.get(); + + coreFrame->tree()->setName(name); + if (ownerElement) { + ASSERT(ownerElement->document()->frame()); + ownerElement->document()->frame()->tree()->appendChild(coreFrame.get()); + } + + coreFrame->init(); + + [webView _setZoomMultiplier:[webView _realZoomMultiplier] isTextOnly:[webView _realZoomMultiplierIsTextOnly]]; + + return coreFrame.release(); +} + ++ (void)_createMainFrameWithPage:(Page*)page frameName:(const String&)name frameView:(WebFrameView *)frameView +{ + [self _createFrameWithPage:page frameName:name frameView:frameView ownerElement:0]; +} + ++ (PassRefPtr<WebCore::Frame>)_createSubframeWithOwnerElement:(HTMLFrameOwnerElement*)ownerElement frameName:(const String&)name frameView:(WebFrameView *)frameView +{ + return [self _createFrameWithPage:ownerElement->document()->frame()->page() frameName:name frameView:frameView ownerElement:ownerElement]; +} + +- (BOOL)_isIncludedInWebKitStatistics +{ + return _private && _private->includedInWebKitStatistics; +} + +- (void)_attachScriptDebugger +{ + ScriptController* scriptController = _private->coreFrame->script(); + + // Calling ScriptController::globalObject() would create a window shell, and dispatch corresponding callbacks, which may be premature + // if the script debugger is attached before a document is created. These calls use the debuggerWorld(), we will need to pass a world + // to be able to debug isolated worlds. + if (!scriptController->existingWindowShell(debuggerWorld())) + return; + + JSGlobalObject* globalObject = scriptController->globalObject(debuggerWorld()); + if (!globalObject) + return; + + if (_private->scriptDebugger) { + ASSERT(_private->scriptDebugger == globalObject->debugger()); + return; + } + + _private->scriptDebugger = new WebScriptDebugger(globalObject); +} + +- (void)_detachScriptDebugger +{ + if (!_private->scriptDebugger) + return; + + delete _private->scriptDebugger; + _private->scriptDebugger = 0; +} + +- (id)_initWithWebFrameView:(WebFrameView *)fv webView:(WebView *)v +{ + self = [super init]; + if (!self) + return nil; + + _private = [[WebFramePrivate alloc] init]; + + // Set includedInWebKitStatistics before calling WebFrameView _setWebFrame, since + // it calls WebFrame _isIncludedInWebKitStatistics. + if ((_private->includedInWebKitStatistics = [[v class] shouldIncludeInWebKitStatistics])) + ++WebFrameCount; + + if (fv) { + [_private setWebFrameView:fv]; + [fv _setWebFrame:self]; + } + + _private->shouldCreateRenderers = YES; + + return self; +} + +- (void)_clearCoreFrame +{ + _private->coreFrame = 0; +} + +- (void)_updateBackgroundAndUpdatesWhileOffscreen +{ + WebView *webView = getWebView(self); + BOOL drawsBackground = [webView drawsBackground]; + NSColor *backgroundColor = [webView backgroundColor]; + + Frame* coreFrame = _private->coreFrame; + for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) { + if ([webView _usesDocumentViews]) { + // Don't call setDrawsBackground:YES here because it may be NO because of a load + // in progress; WebFrameLoaderClient keeps it set to NO during the load process. + WebFrame *webFrame = kit(frame); + if (!drawsBackground) + [[[webFrame frameView] _scrollView] setDrawsBackground:NO]; + [[[webFrame frameView] _scrollView] setBackgroundColor:backgroundColor]; + id documentView = [[webFrame frameView] documentView]; + if ([documentView respondsToSelector:@selector(setDrawsBackground:)]) + [documentView setDrawsBackground:drawsBackground]; + if ([documentView respondsToSelector:@selector(setBackgroundColor:)]) + [documentView setBackgroundColor:backgroundColor]; + } + + if (FrameView* view = frame->view()) { + view->setTransparent(!drawsBackground); + view->setBaseBackgroundColor(colorFromNSColor([backgroundColor colorUsingColorSpaceName:NSDeviceRGBColorSpace])); + view->setShouldUpdateWhileOffscreen([webView shouldUpdateWhileOffscreen]); + } + } +} + +- (void)_setInternalLoadDelegate:(id)internalLoadDelegate +{ + _private->internalLoadDelegate = internalLoadDelegate; +} + +- (id)_internalLoadDelegate +{ + return _private->internalLoadDelegate; +} + +#ifndef BUILDING_ON_TIGER +- (void)_unmarkAllBadGrammar +{ + Frame* coreFrame = _private->coreFrame; + for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) { + if (Document* document = frame->document()) + document->markers()->removeMarkers(DocumentMarker::Grammar); + } +} +#endif + +- (void)_unmarkAllMisspellings +{ + Frame* coreFrame = _private->coreFrame; + for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) { + if (Document* document = frame->document()) + document->markers()->removeMarkers(DocumentMarker::Spelling); + } +} + +- (BOOL)_hasSelection +{ + if ([getWebView(self) _usesDocumentViews]) { + id documentView = [_private->webFrameView documentView]; + + // optimization for common case to avoid creating potentially large selection string + if ([documentView isKindOfClass:[WebHTMLView class]]) + if (Frame* coreFrame = _private->coreFrame) + return coreFrame->selection()->isRange(); + + if ([documentView conformsToProtocol:@protocol(WebDocumentText)]) + return [[documentView selectedString] length] > 0; + + return NO; + } + + Frame* coreFrame = _private->coreFrame; + return coreFrame && coreFrame->selection()->isRange(); +} + +- (void)_clearSelection +{ + ASSERT([getWebView(self) _usesDocumentViews]); + id documentView = [_private->webFrameView documentView]; + if ([documentView conformsToProtocol:@protocol(WebDocumentText)]) + [documentView deselectAll]; +} + +#if !ASSERT_DISABLED +- (BOOL)_atMostOneFrameHasSelection +{ + // FIXME: 4186050 is one known case that makes this debug check fail. + BOOL found = NO; + Frame* coreFrame = _private->coreFrame; + for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) + if ([kit(frame) _hasSelection]) { + if (found) + return NO; + found = YES; + } + return YES; +} +#endif + +- (WebFrame *)_findFrameWithSelection +{ + Frame* coreFrame = _private->coreFrame; + for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) { + WebFrame *webFrame = kit(frame); + if ([webFrame _hasSelection]) + return webFrame; + } + return nil; +} + +- (void)_clearSelectionInOtherFrames +{ + // We rely on WebDocumentSelection protocol implementors to call this method when they become first + // responder. It would be nicer to just notice first responder changes here instead, but there's no + // notification sent when the first responder changes in general (Radar 2573089). + WebFrame *frameWithSelection = [[getWebView(self) mainFrame] _findFrameWithSelection]; + if (frameWithSelection != self) + [frameWithSelection _clearSelection]; + + // While we're in the general area of selection and frames, check that there is only one now. + ASSERT([[getWebView(self) mainFrame] _atMostOneFrameHasSelection]); +} + +static inline WebDataSource *dataSource(DocumentLoader* loader) +{ + return loader ? static_cast<WebDocumentLoaderMac*>(loader)->dataSource() : nil; +} + +- (WebDataSource *)_dataSource +{ + return dataSource(_private->coreFrame->loader()->documentLoader()); +} + +- (NSString *)_stringWithDocumentTypeStringAndMarkupString:(NSString *)markupString +{ + return _private->coreFrame->documentTypeString() + markupString; +} + +- (NSArray *)_nodesFromList:(Vector<Node*> *)nodesVector +{ + size_t size = nodesVector->size(); + NSMutableArray *nodes = [NSMutableArray arrayWithCapacity:size]; + for (size_t i = 0; i < size; ++i) + [nodes addObject:kit((*nodesVector)[i])]; + return nodes; +} + +- (NSString *)_markupStringFromRange:(DOMRange *)range nodes:(NSArray **)nodes +{ + // FIXME: This is always "for interchange". Is that right? See the previous method. + Vector<Node*> nodeList; + NSString *markupString = createMarkup(core(range), nodes ? &nodeList : 0, AnnotateForInterchange); + if (nodes) + *nodes = [self _nodesFromList:&nodeList]; + + return [self _stringWithDocumentTypeStringAndMarkupString:markupString]; +} + +- (NSString *)_selectedString +{ + return _private->coreFrame->displayStringModifiedByEncoding(_private->coreFrame->editor()->selectedText()); +} + +- (NSString *)_stringForRange:(DOMRange *)range +{ + // This will give a system malloc'd buffer that can be turned directly into an NSString + unsigned length; + UChar* buf = plainTextToMallocAllocatedBuffer(core(range), length, true); + + if (!buf) + return [NSString string]; + + // Transfer buffer ownership to NSString + return [[[NSString alloc] initWithCharactersNoCopy:buf length:length freeWhenDone:YES] autorelease]; +} + +- (BOOL)_shouldFlattenCompositingLayers:(CGContextRef)context +{ + // -currentContextDrawingToScreen returns YES for bitmap contexts. + BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen]; + if (isPrinting) + return YES; + + if (!WKCGContextIsBitmapContext(context)) + return NO; + + // If we're drawing into a bitmap, we might be snapshotting, or drawing into a layer-backed view. + if ([getWebView(self) _usesDocumentViews]) { + id documentView = [_private->webFrameView documentView]; + if ([documentView isKindOfClass:[WebHTMLView class]] && [(WebHTMLView *)documentView _web_isDrawingIntoLayer]) + return NO; + } + + return [getWebView(self) _includesFlattenedCompositingLayersWhenDrawingToBitmap]; +} + +- (void)_drawRect:(NSRect)rect contentsOnly:(BOOL)contentsOnly +{ + ASSERT([[NSGraphicsContext currentContext] isFlipped]); + + CGContextRef ctx = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]); + GraphicsContext context(ctx); + + FrameView* view = _private->coreFrame->view(); + + bool shouldFlatten = false; + if (Frame* parentFrame = _private->coreFrame->tree()->parent()) { + // For subframes, we need to inherit the paint behavior from our parent + FrameView* parentView = parentFrame ? parentFrame->view() : 0; + if (parentView) + shouldFlatten = parentView->paintBehavior() & PaintBehaviorFlattenCompositingLayers; + } else + shouldFlatten = [self _shouldFlattenCompositingLayers:ctx]; + + PaintBehavior oldBehavior = PaintBehaviorNormal; + if (shouldFlatten) { + oldBehavior = view->paintBehavior(); + view->setPaintBehavior(oldBehavior | PaintBehaviorFlattenCompositingLayers); + } + + if (contentsOnly) + _private->coreFrame->view()->paintContents(&context, enclosingIntRect(rect)); + else + _private->coreFrame->view()->paint(&context, enclosingIntRect(rect)); + + if (shouldFlatten) + view->setPaintBehavior(oldBehavior); +} + +// Used by pagination code called from AppKit when a standalone web page is printed. +- (NSArray*)_computePageRectsWithPrintWidthScaleFactor:(float)printWidthScaleFactor printHeight:(float)printHeight +{ + NSMutableArray* pages = [NSMutableArray arrayWithCapacity:5]; + if (printWidthScaleFactor <= 0) { + LOG_ERROR("printWidthScaleFactor has bad value %.2f", printWidthScaleFactor); + return pages; + } + + if (printHeight <= 0) { + LOG_ERROR("printHeight has bad value %.2f", printHeight); + return pages; + } + + if (!_private->coreFrame || !_private->coreFrame->document() || !_private->coreFrame->view()) return pages; + RenderView* root = toRenderView(_private->coreFrame->document()->renderer()); + if (!root) return pages; + + FrameView* view = _private->coreFrame->view(); + if (!view) + return pages; + + NSView* documentView = view->documentView(); + if (!documentView) + return pages; + + float docWidth = root->layer()->width(); + float printWidth = docWidth / printWidthScaleFactor; + + PrintContext printContext(_private->coreFrame); + printContext.computePageRectsWithPageSize(FloatSize(printWidth, printHeight), true); + + const Vector<IntRect>& pageRects = printContext.pageRects(); + const size_t pageCount = pageRects.size(); + for (size_t pageNumber = 0; pageNumber < pageCount; ++pageNumber) + [pages addObject: [NSValue valueWithRect: NSRect(pageRects[pageNumber])]]; + return pages; +} + +- (BOOL)_getVisibleRect:(NSRect*)rect +{ + ASSERT_ARG(rect, rect); + if (RenderPart* ownerRenderer = _private->coreFrame->ownerRenderer()) { + if (ownerRenderer->needsLayout()) + return NO; + *rect = ownerRenderer->absoluteClippedOverflowRect(); + return YES; + } + + return NO; +} + +- (NSString *)_stringByEvaluatingJavaScriptFromString:(NSString *)string +{ + return [self _stringByEvaluatingJavaScriptFromString:string forceUserGesture:true]; +} + +- (NSString *)_stringByEvaluatingJavaScriptFromString:(NSString *)string forceUserGesture:(BOOL)forceUserGesture +{ + ASSERT(_private->coreFrame->document()); + + JSValue result = _private->coreFrame->script()->executeScript(string, forceUserGesture).jsValue(); + + if (!_private->coreFrame) // In case the script removed our frame from the page. + return @""; + + // This bizarre set of rules matches behavior from WebKit for Safari 2.0. + // If you don't like it, use -[WebScriptObject evaluateWebScript:] or + // JSEvaluateScript instead, since they have less surprising semantics. + if (!result || (!result.isBoolean() && !result.isString() && !result.isNumber())) + return @""; + + JSLock lock(SilenceAssertionsOnly); + return ustringToString(result.toString(_private->coreFrame->script()->globalObject(mainThreadNormalWorld())->globalExec())); +} + +- (NSRect)_caretRectAtNode:(DOMNode *)node offset:(int)offset affinity:(NSSelectionAffinity)affinity +{ + VisiblePosition visiblePosition(core(node), offset, static_cast<EAffinity>(affinity)); + return visiblePosition.absoluteCaretBounds(); +} + +- (NSRect)_firstRectForDOMRange:(DOMRange *)range +{ + return _private->coreFrame->editor()->firstRectForRange(core(range)); +} + +- (void)_scrollDOMRangeToVisible:(DOMRange *)range +{ + NSRect rangeRect = [self _firstRectForDOMRange:range]; + Node *startNode = core([range startContainer]); + + if (startNode && startNode->renderer()) { + RenderLayer *layer = startNode->renderer()->enclosingLayer(); + if (layer) + layer->scrollRectToVisible(enclosingIntRect(rangeRect), false, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded); + } +} + +- (BOOL)_needsLayout +{ + return _private->coreFrame->view() ? _private->coreFrame->view()->needsLayout() : false; +} + +- (DOMRange *)_rangeByAlteringCurrentSelection:(SelectionController::EAlteration)alteration direction:(SelectionDirection)direction granularity:(TextGranularity)granularity +{ + if (_private->coreFrame->selection()->isNone()) + return nil; + + SelectionController selection; + selection.setSelection(_private->coreFrame->selection()->selection()); + selection.modify(alteration, direction, granularity); + return kit(selection.toNormalizedRange().get()); +} + +- (TextGranularity)_selectionGranularity +{ + return _private->coreFrame->selection()->granularity(); +} + +- (NSRange)_convertToNSRange:(Range *)range +{ + if (!range || !range->startContainer()) + return NSMakeRange(NSNotFound, 0); + + Element* selectionRoot = _private->coreFrame->selection()->rootEditableElement(); + Element* scope = selectionRoot ? selectionRoot : _private->coreFrame->document()->documentElement(); + + // Mouse events may cause TSM to attempt to create an NSRange for a portion of the view + // that is not inside the current editable region. These checks ensure we don't produce + // potentially invalid data when responding to such requests. + if (range->startContainer() != scope && !range->startContainer()->isDescendantOf(scope)) + return NSMakeRange(NSNotFound, 0); + if (range->endContainer() != scope && !range->endContainer()->isDescendantOf(scope)) + return NSMakeRange(NSNotFound, 0); + + RefPtr<Range> testRange = Range::create(scope->document(), scope, 0, range->startContainer(), range->startOffset()); + ASSERT(testRange->startContainer() == scope); + int startPosition = TextIterator::rangeLength(testRange.get()); + + ExceptionCode ec; + testRange->setEnd(range->endContainer(), range->endOffset(), ec); + ASSERT(testRange->startContainer() == scope); + int endPosition = TextIterator::rangeLength(testRange.get()); + + return NSMakeRange(startPosition, endPosition - startPosition); +} + +- (PassRefPtr<Range>)_convertToDOMRange:(NSRange)nsrange +{ + if (nsrange.location > INT_MAX) + return 0; + if (nsrange.length > INT_MAX || nsrange.location + nsrange.length > INT_MAX) + nsrange.length = INT_MAX - nsrange.location; + + // our critical assumption is that we are only called by input methods that + // concentrate on a given area containing the selection + // We have to do this because of text fields and textareas. The DOM for those is not + // directly in the document DOM, so serialization is problematic. Our solution is + // to use the root editable element of the selection start as the positional base. + // That fits with AppKit's idea of an input context. + Element* selectionRoot = _private->coreFrame->selection()->rootEditableElement(); + Element* scope = selectionRoot ? selectionRoot : _private->coreFrame->document()->documentElement(); + return TextIterator::rangeFromLocationAndLength(scope, nsrange.location, nsrange.length); +} + +- (DOMRange *)convertNSRangeToDOMRange:(NSRange)nsrange +{ + // This method exists to maintain compatibility with Leopard's Dictionary.app. <rdar://problem/6002160> + return [self _convertNSRangeToDOMRange:nsrange]; +} + +- (DOMRange *)_convertNSRangeToDOMRange:(NSRange)nsrange +{ + return kit([self _convertToDOMRange:nsrange].get()); +} + +- (NSRange)convertDOMRangeToNSRange:(DOMRange *)range +{ + // This method exists to maintain compatibility with Leopard's Dictionary.app. <rdar://problem/6002160> + return [self _convertDOMRangeToNSRange:range]; +} + +- (NSRange)_convertDOMRangeToNSRange:(DOMRange *)range +{ + return [self _convertToNSRange:core(range)]; +} + +- (DOMRange *)_markDOMRange +{ + return kit(_private->coreFrame->editor()->mark().toNormalizedRange().get()); +} + +// Given proposedRange, returns an extended range that includes adjacent whitespace that should +// be deleted along with the proposed range in order to preserve proper spacing and punctuation of +// the text surrounding the deletion. +- (DOMRange *)_smartDeleteRangeForProposedRange:(DOMRange *)proposedRange +{ + Node* startContainer = core([proposedRange startContainer]); + Node* endContainer = core([proposedRange endContainer]); + if (startContainer == nil || endContainer == nil) + return nil; + + ASSERT(startContainer->document() == endContainer->document()); + + _private->coreFrame->document()->updateLayoutIgnorePendingStylesheets(); + + Position start(startContainer, [proposedRange startOffset]); + Position end(endContainer, [proposedRange endOffset]); + Position newStart = start.upstream().leadingWhitespacePosition(DOWNSTREAM, true); + if (newStart.isNull()) + newStart = start; + Position newEnd = end.downstream().trailingWhitespacePosition(DOWNSTREAM, true); + if (newEnd.isNull()) + newEnd = end; + + newStart = rangeCompliantEquivalent(newStart); + newEnd = rangeCompliantEquivalent(newEnd); + + RefPtr<Range> range = _private->coreFrame->document()->createRange(); + int exception = 0; + range->setStart(newStart.node(), newStart.deprecatedEditingOffset(), exception); + range->setEnd(newStart.node(), newStart.deprecatedEditingOffset(), exception); + return kit(range.get()); +} + +- (DOMDocumentFragment *)_documentFragmentWithMarkupString:(NSString *)markupString baseURLString:(NSString *)baseURLString +{ + if (!_private->coreFrame || !_private->coreFrame->document()) + return nil; + + return kit(createFragmentFromMarkup(_private->coreFrame->document(), markupString, baseURLString, FragmentScriptingNotAllowed).get()); +} + +- (DOMDocumentFragment *)_documentFragmentWithNodesAsParagraphs:(NSArray *)nodes +{ + if (!_private->coreFrame || !_private->coreFrame->document()) + return nil; + + NSEnumerator *nodeEnum = [nodes objectEnumerator]; + Vector<Node*> nodesVector; + DOMNode *node; + while ((node = [nodeEnum nextObject])) + nodesVector.append(core(node)); + + return kit(createFragmentFromNodes(_private->coreFrame->document(), nodesVector).get()); +} + +- (void)_replaceSelectionWithNode:(DOMNode *)node selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace matchStyle:(BOOL)matchStyle +{ + DOMDocumentFragment *fragment = kit(_private->coreFrame->document()->createDocumentFragment().get()); + [fragment appendChild:node]; + [self _replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:smartReplace matchStyle:matchStyle]; +} + +- (void)_insertParagraphSeparatorInQuotedContent +{ + if (_private->coreFrame->selection()->isNone()) + return; + + TypingCommand::insertParagraphSeparatorInQuotedContent(_private->coreFrame->document()); + _private->coreFrame->selection()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded); +} + +- (VisiblePosition)_visiblePositionForPoint:(NSPoint)point +{ + // FIXME: Someone with access to Apple's sources could remove this needless wrapper call. + return _private->coreFrame->visiblePositionForPoint(IntPoint(point)); +} + +- (DOMRange *)_characterRangeAtPoint:(NSPoint)point +{ + VisiblePosition position = [self _visiblePositionForPoint:point]; + if (position.isNull()) + return nil; + + VisiblePosition previous = position.previous(); + if (previous.isNotNull()) { + DOMRange *previousCharacterRange = kit(makeRange(previous, position).get()); + NSRect rect = [self _firstRectForDOMRange:previousCharacterRange]; + if (NSPointInRect(point, rect)) + return previousCharacterRange; + } + + VisiblePosition next = position.next(); + if (next.isNotNull()) { + DOMRange *nextCharacterRange = kit(makeRange(position, next).get()); + NSRect rect = [self _firstRectForDOMRange:nextCharacterRange]; + if (NSPointInRect(point, rect)) + return nextCharacterRange; + } + + return nil; +} + +- (DOMCSSStyleDeclaration *)_typingStyle +{ + if (!_private->coreFrame) + return nil; + RefPtr<CSSMutableStyleDeclaration> typingStyle = _private->coreFrame->selection()->copyTypingStyle(); + if (!typingStyle) + return nil; + return kit(typingStyle.get()); +} + +- (void)_setTypingStyle:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction +{ + if (!_private->coreFrame) + return; + _private->coreFrame->editor()->computeAndSetTypingStyle(core(style), undoAction); +} + +- (void)_dragSourceEndedAt:(NSPoint)windowLoc operation:(NSDragOperation)operation +{ + if (!_private->coreFrame) + return; + FrameView* view = _private->coreFrame->view(); + if (!view) + return; + ASSERT([getWebView(self) _usesDocumentViews]); + // FIXME: These are fake modifier keys here, but they should be real ones instead. + PlatformMouseEvent event(IntPoint(windowLoc), globalPoint(windowLoc, [view->platformWidget() window]), + LeftButton, MouseEventMoved, 0, false, false, false, false, currentTime()); + _private->coreFrame->eventHandler()->dragSourceEndedAt(event, (DragOperation)operation); +} + +- (BOOL)_canProvideDocumentSource +{ + Frame* frame = _private->coreFrame; + String mimeType = frame->loader()->writer()->mimeType(); + PluginData* pluginData = frame->page() ? frame->page()->pluginData() : 0; + + if (WebCore::DOMImplementation::isTextMIMEType(mimeType) || + Image::supportsType(mimeType) || + (pluginData && pluginData->supportsMimeType(mimeType))) + return NO; + + return YES; +} + +- (BOOL)_canSaveAsWebArchive +{ + // Currently, all documents that we can view source for + // (HTML and XML documents) can also be saved as web archives + return [self _canProvideDocumentSource]; +} + +- (void)_commitData:(NSData *)data +{ + // FIXME: This really should be a setting. + Document* document = _private->coreFrame->document(); + document->setShouldCreateRenderers(_private->shouldCreateRenderers); + + _private->coreFrame->loader()->documentLoader()->commitData((const char *)[data bytes], [data length]); +} + +@end + +@implementation WebFrame (WebPrivate) + +// FIXME: This exists only as a convenience for Safari, consider moving there. +- (BOOL)_isDescendantOfFrame:(WebFrame *)ancestor +{ + Frame* coreFrame = _private->coreFrame; + return coreFrame && coreFrame->tree()->isDescendantOf(core(ancestor)); +} + +- (void)_setShouldCreateRenderers:(BOOL)shouldCreateRenderers +{ + _private->shouldCreateRenderers = shouldCreateRenderers; +} + +- (NSColor *)_bodyBackgroundColor +{ + Document* document = _private->coreFrame->document(); + if (!document) + return nil; + HTMLElement* body = document->body(); + if (!body) + return nil; + RenderObject* bodyRenderer = body->renderer(); + if (!bodyRenderer) + return nil; + Color color = bodyRenderer->style()->visitedDependentColor(CSSPropertyBackgroundColor); + if (!color.isValid()) + return nil; + return nsColor(color); +} + +- (BOOL)_isFrameSet +{ + Document* document = _private->coreFrame->document(); + return document && document->isFrameSet(); +} + +- (BOOL)_firstLayoutDone +{ + return _private->coreFrame->loader()->stateMachine()->firstLayoutDone(); +} + +- (WebFrameLoadType)_loadType +{ + return (WebFrameLoadType)_private->coreFrame->loader()->loadType(); +} + +- (NSRange)_selectedNSRange +{ + return [self _convertToNSRange:_private->coreFrame->selection()->toNormalizedRange().get()]; +} + +- (void)_selectNSRange:(NSRange)range +{ + RefPtr<Range> domRange = [self _convertToDOMRange:range]; + if (domRange) + _private->coreFrame->selection()->setSelection(VisibleSelection(domRange.get(), SEL_DEFAULT_AFFINITY)); +} + +- (BOOL)_isDisplayingStandaloneImage +{ + Document* document = _private->coreFrame->document(); + return document && document->isImageDocument(); +} + +- (unsigned)_pendingFrameUnloadEventCount +{ + return _private->coreFrame->domWindow()->pendingUnloadEventListeners(); +} + +- (void)_setIsDisconnected:(bool)isDisconnected +{ + _private->coreFrame->setIsDisconnected(isDisconnected); +} + +- (void)_setExcludeFromTextSearch:(bool)exclude +{ + _private->coreFrame->setExcludeFromTextSearch(exclude); +} + +#if ENABLE(NETSCAPE_PLUGIN_API) +- (void)_recursive_resumeNullEventsForAllNetscapePlugins +{ + Frame* coreFrame = core(self); + for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) { + NSView <WebDocumentView> *documentView = [[kit(frame) frameView] documentView]; + if ([documentView isKindOfClass:[WebHTMLView class]]) + [(WebHTMLView *)documentView _resumeNullEventsForAllNetscapePlugins]; + } +} + +- (void)_recursive_pauseNullEventsForAllNetscapePlugins +{ + Frame* coreFrame = core(self); + for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) { + NSView <WebDocumentView> *documentView = [[kit(frame) frameView] documentView]; + if ([documentView isKindOfClass:[WebHTMLView class]]) + [(WebHTMLView *)documentView _pauseNullEventsForAllNetscapePlugins]; + } +} +#endif + +- (BOOL)_pauseAnimation:(NSString*)name onNode:(DOMNode *)node atTime:(NSTimeInterval)time +{ + Frame* frame = core(self); + if (!frame) + return false; + + AnimationController* controller = frame->animation(); + if (!controller) + return false; + + Node* coreNode = core(node); + if (!coreNode || !coreNode->renderer()) + return false; + + return controller->pauseAnimationAtTime(coreNode->renderer(), name, time); +} + +- (BOOL)_pauseTransitionOfProperty:(NSString*)name onNode:(DOMNode*)node atTime:(NSTimeInterval)time +{ + Frame* frame = core(self); + if (!frame) + return false; + + AnimationController* controller = frame->animation(); + if (!controller) + return false; + + Node* coreNode = core(node); + if (!coreNode || !coreNode->renderer()) + return false; + + return controller->pauseTransitionAtTime(coreNode->renderer(), name, time); +} + +// Pause a given SVG animation on the target node at a specific time. +// This method is only intended to be used for testing the SVG animation system. +- (BOOL)_pauseSVGAnimation:(NSString*)elementId onSMILNode:(DOMNode *)node atTime:(NSTimeInterval)time +{ + Frame* frame = core(self); + if (!frame) + return false; + + Document* document = frame->document(); + if (!document || !document->svgExtensions()) + return false; + + Node* coreNode = core(node); + if (!coreNode || !SVGSMILElement::isSMILElement(coreNode)) + return false; + +#if ENABLE(SVG) + return document->accessSVGExtensions()->sampleAnimationAtTime(elementId, static_cast<SVGSMILElement*>(coreNode), time); +#else + return false; +#endif +} + +- (unsigned) _numberOfActiveAnimations +{ + Frame* frame = core(self); + if (!frame) + return false; + + AnimationController* controller = frame->animation(); + if (!controller) + return false; + + return controller->numberOfActiveAnimations(); +} + +- (void) _suspendAnimations +{ + Frame* frame = core(self); + if (!frame) + return; + + frame->animation()->suspendAnimations(); +} + +- (void) _resumeAnimations +{ + Frame* frame = core(self); + if (!frame) + return; + + frame->animation()->resumeAnimations(); +} + +- (void)_replaceSelectionWithFragment:(DOMDocumentFragment *)fragment selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace matchStyle:(BOOL)matchStyle +{ + if (_private->coreFrame->selection()->isNone() || !fragment) + return; + + applyCommand(ReplaceSelectionCommand::create(_private->coreFrame->document(), core(fragment), selectReplacement, smartReplace, matchStyle)); + _private->coreFrame->selection()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded); +} + +- (void)_replaceSelectionWithText:(NSString *)text selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace +{ + DOMDocumentFragment* fragment = kit(createFragmentFromText(_private->coreFrame->selection()->toNormalizedRange().get(), text).get()); + [self _replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:smartReplace matchStyle:YES]; +} + +- (void)_replaceSelectionWithMarkupString:(NSString *)markupString baseURLString:(NSString *)baseURLString selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace +{ + DOMDocumentFragment *fragment = [self _documentFragmentWithMarkupString:markupString baseURLString:baseURLString]; + [self _replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:smartReplace matchStyle:NO]; +} + +// Determines whether whitespace needs to be added around aString to preserve proper spacing and +// punctuation when it's inserted into the receiver's text over charRange. Returns by reference +// in beforeString and afterString any whitespace that should be added, unless either or both are +// nil. Both are returned as nil if aString is nil or if smart insertion and deletion are disabled. +- (void)_smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)rangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString +{ + // give back nil pointers in case of early returns + if (beforeString) + *beforeString = nil; + if (afterString) + *afterString = nil; + + // inspect destination + Node *startContainer = core([rangeToReplace startContainer]); + Node *endContainer = core([rangeToReplace endContainer]); + + Position startPos(startContainer, [rangeToReplace startOffset]); + Position endPos(endContainer, [rangeToReplace endOffset]); + + VisiblePosition startVisiblePos = VisiblePosition(startPos, VP_DEFAULT_AFFINITY); + VisiblePosition endVisiblePos = VisiblePosition(endPos, VP_DEFAULT_AFFINITY); + + // this check also ensures startContainer, startPos, endContainer, and endPos are non-null + if (startVisiblePos.isNull() || endVisiblePos.isNull()) + return; + + bool addLeadingSpace = startPos.leadingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNull() && !isStartOfParagraph(startVisiblePos); + if (addLeadingSpace) + if (UChar previousChar = startVisiblePos.previous().characterAfter()) + addLeadingSpace = !isCharacterSmartReplaceExempt(previousChar, true); + + bool addTrailingSpace = endPos.trailingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNull() && !isEndOfParagraph(endVisiblePos); + if (addTrailingSpace) + if (UChar thisChar = endVisiblePos.characterAfter()) + addTrailingSpace = !isCharacterSmartReplaceExempt(thisChar, false); + + // inspect source + bool hasWhitespaceAtStart = false; + bool hasWhitespaceAtEnd = false; + unsigned pasteLength = [pasteString length]; + if (pasteLength > 0) { + NSCharacterSet *whiteSet = [NSCharacterSet whitespaceAndNewlineCharacterSet]; + + if ([whiteSet characterIsMember:[pasteString characterAtIndex:0]]) { + hasWhitespaceAtStart = YES; + } + if ([whiteSet characterIsMember:[pasteString characterAtIndex:(pasteLength - 1)]]) { + hasWhitespaceAtEnd = YES; + } + } + + // issue the verdict + if (beforeString && addLeadingSpace && !hasWhitespaceAtStart) + *beforeString = @" "; + if (afterString && addTrailingSpace && !hasWhitespaceAtEnd) + *afterString = @" "; +} + +- (NSMutableDictionary *)_cacheabilityDictionary +{ + NSMutableDictionary *result = [NSMutableDictionary dictionary]; + + FrameLoader* frameLoader = _private->coreFrame->loader(); + DocumentLoader* documentLoader = frameLoader->documentLoader(); + if (documentLoader && !documentLoader->mainDocumentError().isNull()) + [result setObject:(NSError *)documentLoader->mainDocumentError() forKey:WebFrameMainDocumentError]; + + if (frameLoader->subframeLoader()->containsPlugins()) + [result setObject:[NSNumber numberWithBool:YES] forKey:WebFrameHasPlugins]; + + if (DOMWindow* domWindow = _private->coreFrame->domWindow()) { + if (domWindow->hasEventListeners(eventNames().unloadEvent)) + [result setObject:[NSNumber numberWithBool:YES] forKey:WebFrameHasUnloadListener]; + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + if (domWindow->optionalApplicationCache()) + [result setObject:[NSNumber numberWithBool:YES] forKey:WebFrameUsesApplicationCache]; +#endif + } + + if (Document* document = _private->coreFrame->document()) { +#if ENABLE(DATABASE) + if (document->hasOpenDatabases()) + [result setObject:[NSNumber numberWithBool:YES] forKey:WebFrameUsesDatabases]; +#endif + + if (document->usingGeolocation()) + [result setObject:[NSNumber numberWithBool:YES] forKey:WebFrameUsesGeolocation]; + + if (!document->canSuspendActiveDOMObjects()) + [result setObject:[NSNumber numberWithBool:YES] forKey:WebFrameCanSuspendActiveDOMObjects]; + } + + return result; +} + +- (BOOL)_allowsFollowingLink:(NSURL *)URL +{ + if (!_private->coreFrame) + return YES; + return _private->coreFrame->document()->securityOrigin()->canDisplay(URL); +} + +- (NSString *)_stringByEvaluatingJavaScriptFromString:(NSString *)string withGlobalObject:(JSObjectRef)globalObjectRef inScriptWorld:(WebScriptWorld *)world +{ + // Start off with some guess at a frame and a global object, we'll try to do better...! + JSDOMWindow* anyWorldGlobalObject = _private->coreFrame->script()->globalObject(mainThreadNormalWorld()); + + // The global object is probably a shell object? - if so, we know how to use this! + JSC::JSObject* globalObjectObj = toJS(globalObjectRef); + if (!strcmp(globalObjectObj->classInfo()->className, "JSDOMWindowShell")) + anyWorldGlobalObject = static_cast<JSDOMWindowShell*>(globalObjectObj)->window(); + + // Get the frame frome the global object we've settled on. + Frame* frame = anyWorldGlobalObject->impl()->frame(); + ASSERT(frame->document()); + JSValue result = frame->script()->executeScriptInWorld(core(world), string, true).jsValue(); + + if (!frame) // In case the script removed our frame from the page. + return @""; + + // This bizarre set of rules matches behavior from WebKit for Safari 2.0. + // If you don't like it, use -[WebScriptObject evaluateWebScript:] or + // JSEvaluateScript instead, since they have less surprising semantics. + if (!result || (!result.isBoolean() && !result.isString() && !result.isNumber())) + return @""; + + JSLock lock(SilenceAssertionsOnly); + return ustringToString(result.toString(anyWorldGlobalObject->globalExec())); +} + +- (JSGlobalContextRef)_globalContextForScriptWorld:(WebScriptWorld *)world +{ + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return 0; + DOMWrapperWorld* coreWorld = core(world); + if (!coreWorld) + return 0; + return toGlobalRef(coreFrame->script()->globalObject(coreWorld)->globalExec()); +} + +- (void)setAllowsScrollersToOverlapContent:(BOOL)flag +{ + ASSERT([[[self frameView] _scrollView] isKindOfClass:[WebDynamicScrollBarsView class]]); + [(WebDynamicScrollBarsView *)[[self frameView] _scrollView] setAllowsScrollersToOverlapContent:flag]; +} + +- (void)setAlwaysHideHorizontalScroller:(BOOL)flag +{ + ASSERT([[[self frameView] _scrollView] isKindOfClass:[WebDynamicScrollBarsView class]]); + [(WebDynamicScrollBarsView *)[[self frameView] _scrollView] setAlwaysHideHorizontalScroller:flag]; +} +- (void)setAlwaysHideVerticalScroller:(BOOL)flag +{ + ASSERT([[[self frameView] _scrollView] isKindOfClass:[WebDynamicScrollBarsView class]]); + [(WebDynamicScrollBarsView *)[[self frameView] _scrollView] setAlwaysHideVerticalScroller:flag]; +} + +- (void)setAccessibleName:(NSString *)name +{ +#if HAVE(ACCESSIBILITY) + if (!AXObjectCache::accessibilityEnabled()) + return; + + if (!_private->coreFrame || !_private->coreFrame->document()) + return; + + AccessibilityObject* rootObject = _private->coreFrame->document()->axObjectCache()->rootObject(); + if (rootObject) { + String strName(name); + rootObject->setAccessibleName(strName); + } +#endif +} + +- (NSString*)_layerTreeAsText +{ + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return @""; + + return coreFrame->layerTreeAsText(); +} + +- (BOOL)hasSpellingMarker:(int)from length:(int)length +{ + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return NO; + return coreFrame->editor()->selectionStartHasSpellingMarkerFor(from, length); +} + +- (id)accessibilityRoot +{ +#if HAVE(ACCESSIBILITY) + if (!AXObjectCache::accessibilityEnabled()) { + AXObjectCache::enableAccessibility(); + if ([[NSApp accessibilityAttributeValue:NSAccessibilityEnhancedUserInterfaceAttribute] boolValue]) + AXObjectCache::enableEnhancedUserInterfaceAccessibility(); + } + + if (!_private->coreFrame || !_private->coreFrame->document()) + return nil; + + AccessibilityObject* rootObject = _private->coreFrame->document()->axObjectCache()->rootObject(); + if (rootObject) + return rootObject->wrapper(); + return nil; +#else + return nil; +#endif +} + +@end + +@implementation WebFrame + +- (id)init +{ + return nil; +} + +// Should be deprecated. +- (id)initWithName:(NSString *)name webFrameView:(WebFrameView *)view webView:(WebView *)webView +{ + return nil; +} + +- (void)dealloc +{ + if (_private && _private->includedInWebKitStatistics) + --WebFrameCount; + + [_private release]; + + [super dealloc]; +} + +- (void)finalize +{ + if (_private && _private->includedInWebKitStatistics) + --WebFrameCount; + + [super finalize]; +} + +- (NSString *)name +{ + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return nil; + return coreFrame->tree()->uniqueName(); +} + +- (WebFrameView *)frameView +{ + ASSERT(!getWebView(self) || [getWebView(self) _usesDocumentViews]); + return _private->webFrameView; +} + +- (WebView *)webView +{ + return getWebView(self); +} + +static bool needsMicrosoftMessengerDOMDocumentWorkaround() +{ + static bool needsWorkaround = applicationIsMicrosoftMessenger() && [[[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey] compare:@"7.1" options:NSNumericSearch] == NSOrderedAscending; + return needsWorkaround; +} + +- (DOMDocument *)DOMDocument +{ + if (needsMicrosoftMessengerDOMDocumentWorkaround() && !pthread_main_np()) + return nil; + + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return nil; + + // FIXME: <rdar://problem/5145841> When loading a custom view/representation + // into a web frame, the old document can still be around. This makes sure that + // we'll return nil in those cases. + if (![[self _dataSource] _isDocumentHTML]) + return nil; + + Document* document = coreFrame->document(); + + // According to the documentation, we should return nil if the frame doesn't have a document. + // While full-frame images and plugins do have an underlying HTML document, we return nil here to be + // backwards compatible. + if (document && (document->isPluginDocument() || document->isImageDocument())) + return nil; + + return kit(coreFrame->document()); +} + +- (DOMHTMLElement *)frameElement +{ + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return nil; + return kit(coreFrame->ownerElement()); +} + +- (WebDataSource *)provisionalDataSource +{ + Frame* coreFrame = _private->coreFrame; + return coreFrame ? dataSource(coreFrame->loader()->provisionalDocumentLoader()) : nil; +} + +- (WebDataSource *)dataSource +{ + Frame* coreFrame = _private->coreFrame; + return coreFrame && coreFrame->loader()->frameHasLoaded() ? [self _dataSource] : nil; +} + +- (void)loadRequest:(NSURLRequest *)request +{ + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return; + coreFrame->loader()->load(request, false); +} + +static NSURL *createUniqueWebDataURL() +{ + CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault); + NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef); + CFRelease(UUIDRef); + NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"applewebdata://%@", UUIDString]]; + CFRelease(UUIDString); + return URL; +} + +- (void)_loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL unreachableURL:(NSURL *)unreachableURL +{ + if (!pthread_main_np()) + return [[self _webkit_invokeOnMainThread] _loadData:data MIMEType:MIMEType textEncodingName:encodingName baseURL:baseURL unreachableURL:unreachableURL]; + + KURL responseURL; + if (!baseURL) { + baseURL = blankURL(); + responseURL = createUniqueWebDataURL(); + } + + ResourceRequest request([baseURL absoluteURL]); + + // hack because Mail checks for this property to detect data / archive loads + [NSURLProtocol setProperty:@"" forKey:@"WebDataRequest" inRequest:(NSMutableURLRequest *)request.nsURLRequest()]; + + SubstituteData substituteData(WebCore::SharedBuffer::wrapNSData(data), MIMEType, encodingName, [unreachableURL absoluteURL], responseURL); + + _private->coreFrame->loader()->load(request, substituteData, false); +} + + +- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL +{ + WebCoreThreadViolationCheckRoundTwo(); + + if (!MIMEType) + MIMEType = @"text/html"; + [self _loadData:data MIMEType:MIMEType textEncodingName:encodingName baseURL:baseURL unreachableURL:nil]; +} + +- (void)_loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL unreachableURL:(NSURL *)unreachableURL +{ + NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding]; + [self _loadData:data MIMEType:@"text/html" textEncodingName:@"UTF-8" baseURL:baseURL unreachableURL:unreachableURL]; +} + +- (void)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL +{ + WebCoreThreadViolationCheckRoundTwo(); + + [self _loadHTMLString:string baseURL:baseURL unreachableURL:nil]; +} + +- (void)loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)baseURL forUnreachableURL:(NSURL *)unreachableURL +{ + WebCoreThreadViolationCheckRoundTwo(); + + [self _loadHTMLString:string baseURL:baseURL unreachableURL:unreachableURL]; +} + +- (void)loadArchive:(WebArchive *)archive +{ + if (LegacyWebArchive* coreArchive = [archive _coreLegacyWebArchive]) + _private->coreFrame->loader()->loadArchive(coreArchive); +} + +- (void)stopLoading +{ + if (!_private->coreFrame) + return; + _private->coreFrame->loader()->stopForUserCancel(); +} + +- (void)reload +{ + if (!WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_RELOAD_FROM_ORIGIN) && applicationIsSafari()) + _private->coreFrame->loader()->reload(GetCurrentKeyModifiers() & shiftKey); + else + _private->coreFrame->loader()->reload(false); +} + +- (void)reloadFromOrigin +{ + _private->coreFrame->loader()->reload(true); +} + +- (WebFrame *)findFrameNamed:(NSString *)name +{ + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return nil; + return kit(coreFrame->tree()->find(name)); +} + +- (WebFrame *)parentFrame +{ + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return nil; + return [[kit(coreFrame->tree()->parent()) retain] autorelease]; +} + +- (NSArray *)childFrames +{ + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return [NSArray array]; + NSMutableArray *children = [NSMutableArray arrayWithCapacity:coreFrame->tree()->childCount()]; + for (Frame* child = coreFrame->tree()->firstChild(); child; child = child->tree()->nextSibling()) + [children addObject:kit(child)]; + return children; +} + +- (WebScriptObject *)windowObject +{ + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return 0; + return coreFrame->script()->windowScriptObject(); +} + +- (JSGlobalContextRef)globalContext +{ + Frame* coreFrame = _private->coreFrame; + if (!coreFrame) + return 0; + return toGlobalRef(coreFrame->script()->globalObject(mainThreadNormalWorld())->globalExec()); +} + +@end diff --git a/Source/WebKit/mac/WebView/WebFrameInternal.h b/Source/WebKit/mac/WebView/WebFrameInternal.h new file mode 100644 index 0000000..7ff5e75 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebFrameInternal.h @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * 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. + */ + +// This header contains WebFrame declarations that can be used anywhere in WebKit, but are neither SPI nor API. + +#import "WebFramePrivate.h" +#import "WebPreferencesPrivate.h" +#import <WebCore/EditAction.h> +#import <WebCore/FrameLoaderTypes.h> +#import <WebCore/SelectionController.h> +#import <WebCore/Settings.h> + +@class DOMCSSStyleDeclaration; +@class DOMDocumentFragment; +@class DOMElement; +@class DOMNode; +@class DOMRange; +@class WebFrameView; +@class WebHistoryItem; + +class WebScriptDebugger; + +namespace WebCore { + class CSSStyleDeclaration; + class Document; + class DocumentLoader; + class Element; + class Frame; + class Frame; + class HistoryItem; + class HTMLElement; + class HTMLFrameOwnerElement; + class Node; + class Page; + class Range; +} + +typedef WebCore::HistoryItem WebCoreHistoryItem; + +WebCore::Frame* core(WebFrame *); +WebFrame *kit(WebCore::Frame *); + +WebCore::Page* core(WebView *); +WebView *kit(WebCore::Page*); + +WebCore::EditableLinkBehavior core(WebKitEditableLinkBehavior); +WebCore::TextDirectionSubmenuInclusionBehavior core(WebTextDirectionSubmenuInclusionBehavior); +WebCore::EditingBehaviorType core(WebKitEditingBehavior); + +WebView *getWebView(WebFrame *webFrame); + +@interface WebFramePrivate : NSObject { +@public + WebCore::Frame* coreFrame; + WebFrameView *webFrameView; + WebScriptDebugger* scriptDebugger; + id internalLoadDelegate; + BOOL shouldCreateRenderers; + BOOL includedInWebKitStatistics; +} +@end + +@protocol WebCoreRenderTreeCopier <NSObject> +- (NSObject *)nodeWithName:(NSString *)name position:(NSPoint)position rect:(NSRect)rect view:(NSView *)view children:(NSArray *)children; +@end + +@interface WebFrame (WebInternal) + ++ (void)_createMainFrameWithPage:(WebCore::Page*)page frameName:(const WTF::String&)name frameView:(WebFrameView *)frameView; ++ (PassRefPtr<WebCore::Frame>)_createSubframeWithOwnerElement:(WebCore::HTMLFrameOwnerElement*)ownerElement frameName:(const WTF::String&)name frameView:(WebFrameView *)frameView; +- (id)_initWithWebFrameView:(WebFrameView *)webFrameView webView:(WebView *)webView; + +- (void)_clearCoreFrame; + +- (BOOL)_isIncludedInWebKitStatistics; + +- (void)_updateBackgroundAndUpdatesWhileOffscreen; +- (void)_setInternalLoadDelegate:(id)internalLoadDelegate; +- (id)_internalLoadDelegate; +#ifndef BUILDING_ON_TIGER +- (void)_unmarkAllBadGrammar; +#endif +- (void)_unmarkAllMisspellings; + +- (BOOL)_hasSelection; +- (void)_clearSelection; +- (WebFrame *)_findFrameWithSelection; +- (void)_clearSelectionInOtherFrames; + +- (void)_attachScriptDebugger; +- (void)_detachScriptDebugger; + +// dataSource reports null for the initial empty document's data source; this is needed +// to preserve compatibility with Mail and Safari among others. But internal to WebKit, +// we need to be able to get the initial data source as well, so the _dataSource method +// should be used instead. +- (WebDataSource *)_dataSource; + +- (BOOL)_needsLayout; +- (void)_drawRect:(NSRect)rect contentsOnly:(BOOL)contentsOnly; +- (BOOL)_getVisibleRect:(NSRect*)rect; +- (NSArray*)_computePageRectsWithPrintWidthScaleFactor:(float)printWidthScaleFactor printHeight:(float)printHeight; + +- (NSString *)_stringByEvaluatingJavaScriptFromString:(NSString *)string; +- (NSString *)_stringByEvaluatingJavaScriptFromString:(NSString *)string forceUserGesture:(BOOL)forceUserGesture; + +- (NSString *)_selectedString; +- (NSString *)_stringForRange:(DOMRange *)range; + +- (NSString *)_markupStringFromRange:(DOMRange *)range nodes:(NSArray **)nodes; + +- (NSRect)_caretRectAtNode:(DOMNode *)node offset:(int)offset affinity:(NSSelectionAffinity)affinity; +- (NSRect)_firstRectForDOMRange:(DOMRange *)range; +- (void)_scrollDOMRangeToVisible:(DOMRange *)range; + +- (DOMRange *)_rangeByAlteringCurrentSelection:(WebCore::SelectionController::EAlteration)alteration direction:(WebCore::SelectionDirection)direction granularity:(WebCore::TextGranularity)granularity; +- (NSRange)_convertToNSRange:(WebCore::Range*)range; +- (DOMRange *)_convertNSRangeToDOMRange:(NSRange)range; +- (NSRange)_convertDOMRangeToNSRange:(DOMRange *)range; + +- (DOMDocumentFragment *)_documentFragmentWithMarkupString:(NSString *)markupString baseURLString:(NSString *)baseURLString; +- (DOMDocumentFragment *)_documentFragmentWithNodesAsParagraphs:(NSArray *)nodes; + +- (void)_replaceSelectionWithNode:(DOMNode *)node selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace matchStyle:(BOOL)matchStyle; + +- (void)_insertParagraphSeparatorInQuotedContent; + +- (DOMRange *)_characterRangeAtPoint:(NSPoint)point; + +- (DOMCSSStyleDeclaration *)_typingStyle; +- (void)_setTypingStyle:(DOMCSSStyleDeclaration *)style withUndoAction:(WebCore::EditAction)undoAction; + +- (void)_dragSourceEndedAt:(NSPoint)windowLoc operation:(NSDragOperation)operation; + +- (BOOL)_canProvideDocumentSource; +- (BOOL)_canSaveAsWebArchive; + +- (void)_commitData:(NSData *)data; + +@end + +@interface NSObject (WebInternalFrameLoadDelegate) +- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithError:(NSError *)error; +@end diff --git a/Source/WebKit/mac/WebView/WebFrameLoadDelegate.h b/Source/WebKit/mac/WebView/WebFrameLoadDelegate.h new file mode 100644 index 0000000..e5f1c7c --- /dev/null +++ b/Source/WebKit/mac/WebView/WebFrameLoadDelegate.h @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2003, 2004, 2005 Apple Computer, Inc. All rights reserved. + * + * 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 <Cocoa/Cocoa.h> +#import <JavaScriptCore/WebKitAvailability.h> + +@class NSError; +@class WebFrame; +@class WebScriptObject; +@class WebView; + +/*! + @category WebFrameLoadDelegate + @discussion A WebView's WebFrameLoadDelegate tracks the loading progress of its frames. + When a data source of a frame starts to load, the data source is considered "provisional". + Once at least one byte is received, the data source is considered "committed". This is done + so the contents of the frame will not be lost if the new data source fails to successfully load. +*/ +@interface NSObject (WebFrameLoadDelegate) + +/*! + @method webView:didStartProvisionalLoadForFrame: + @abstract Notifies the delegate that the provisional load of a frame has started + @param webView The WebView sending the message + @param frame The frame for which the provisional load has started + @discussion This method is called after the provisional data source of a frame + has started to load. +*/ +- (void)webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame; + +/*! + @method webView:didReceiveServerRedirectForProvisionalLoadForFrame: + @abstract Notifies the delegate that a server redirect occurred during the provisional load + @param webView The WebView sending the message + @param frame The frame for which the redirect occurred +*/ +- (void)webView:(WebView *)sender didReceiveServerRedirectForProvisionalLoadForFrame:(WebFrame *)frame; + +/*! + @method webView:didFailProvisionalLoadWithError:forFrame: + @abstract Notifies the delegate that the provisional load has failed + @param webView The WebView sending the message + @param error The error that occurred + @param frame The frame for which the error occurred + @discussion This method is called after the provisional data source has failed to load. + The frame will continue to display the contents of the committed data source if there is one. +*/ +- (void)webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame; + +/*! + @method webView:didCommitLoadForFrame: + @abstract Notifies the delegate that the load has changed from provisional to committed + @param webView The WebView sending the message + @param frame The frame for which the load has committed + @discussion This method is called after the provisional data source has become the + committed data source. + + In some cases, a single load may be committed more than once. This happens + in the case of multipart/x-mixed-replace, also known as "server push". In this case, + a single location change leads to multiple documents that are loaded in sequence. When + this happens, a new commit will be sent for each document. +*/ +- (void)webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame; + +/*! + @method webView:didReceiveTitle:forFrame: + @abstract Notifies the delegate that the page title for a frame has been received + @param webView The WebView sending the message + @param title The new page title + @param frame The frame for which the title has been received + @discussion The title may update during loading; clients should be prepared for this. +*/ +- (void)webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame; + +/*! + @method webView:didReceiveIcon:forFrame: + @abstract Notifies the delegate that a page icon image for a frame has been received + @param webView The WebView sending the message + @param image The icon image. Also known as a "favicon". + @param frame The frame for which a page icon has been received +*/ +- (void)webView:(WebView *)sender didReceiveIcon:(NSImage *)image forFrame:(WebFrame *)frame; + +/*! + @method webView:didFinishLoadForFrame: + @abstract Notifies the delegate that the committed load of a frame has completed + @param webView The WebView sending the message + @param frame The frame that finished loading + @discussion This method is called after the committed data source of a frame has successfully loaded + and will only be called when all subresources such as images and stylesheets are done loading. + Plug-In content and JavaScript-requested loads may occur after this method is called. +*/ +- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame; + +/*! + @method webView:didFailLoadWithError:forFrame: + @abstract Notifies the delegate that the committed load of a frame has failed + @param webView The WebView sending the message + @param error The error that occurred + @param frame The frame that failed to load + @discussion This method is called after a data source has committed but failed to completely load. +*/ +- (void)webView:(WebView *)sender didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame; + +/*! + @method webView:didChangeLocationWithinPageForFrame: + @abstract Notifies the delegate that the scroll position in a frame has changed + @param webView The WebView sending the message + @param frame The frame that scrolled + @discussion This method is called when anchors within a page have been clicked. +*/ +- (void)webView:(WebView *)sender didChangeLocationWithinPageForFrame:(WebFrame *)frame; + +/*! + @method webView:willPerformClientRedirectToURL:delay:fireDate:forFrame: + @abstract Notifies the delegate that a frame will perform a client-side redirect + @param webView The WebView sending the message + @param URL The URL to be redirected to + @param seconds Seconds in which the redirect will happen + @param date The fire date + @param frame The frame on which the redirect will occur + @discussion This method can be used to continue progress feedback while a client-side + redirect is pending. +*/ +- (void)webView:(WebView *)sender willPerformClientRedirectToURL:(NSURL *)URL delay:(NSTimeInterval)seconds fireDate:(NSDate *)date forFrame:(WebFrame *)frame; + +/*! + @method webView:didCancelClientRedirectForFrame: + @abstract Notifies the delegate that a pending client-side redirect has been cancelled + @param webView The WebView sending the message + @param frame The frame for which the pending redirect was cancelled + @discussion A client-side redirect can be cancelled if a frame changes location before the timeout. +*/ +- (void)webView:(WebView *)sender didCancelClientRedirectForFrame:(WebFrame *)frame; + +/*! + @method webView:willCloseFrame: + @abstract Notifies the delegate that a frame will be closed + @param webView The WebView sending the message + @param frame The frame that will be closed + @discussion This method is called right before WebKit is done with the frame + and the objects that it contains. +*/ +- (void)webView:(WebView *)sender willCloseFrame:(WebFrame *)frame; + +/*! + @method webView:didClearWindowObject:forFrame: + @abstract Notifies the delegate that the JavaScript window object in a frame has + been cleared in preparation for a new load. This is the preferred place to set custom + properties on the window object using the WebScriptObject and JavaScriptCore APIs. + @param webView The webView sending the message. + @param windowObject The WebScriptObject representing the frame's JavaScript window object. + @param frame The WebFrame to which windowObject belongs. + @discussion If a delegate implements both webView:didClearWindowObject:forFrame: + and webView:windowScriptObjectAvailable:, only webView:didClearWindowObject:forFrame: + will be invoked. This enables a delegate to implement both methods for backwards + compatibility with older versions of WebKit. +*/ +- (void)webView:(WebView *)webView didClearWindowObject:(WebScriptObject *)windowObject forFrame:(WebFrame *)frame; + +/*! + @method webView:windowScriptObjectAvailable: + @abstract Notifies the delegate that the scripting object for a page is available. This is called + before the page is loaded. It may be useful to allow delegates to bind native objects to the window. + @param webView The webView sending the message. + @param windowScriptObject The WebScriptObject for the window in the scripting environment. + @discussion This method is deprecated. Consider using webView:didClearWindowObject:forFrame: + instead. +*/ +- (void)webView:(WebView *)webView windowScriptObjectAvailable:(WebScriptObject *)windowScriptObject WEBKIT_OBJC_METHOD_ANNOTATION(AVAILABLE_WEBKIT_VERSION_1_3_AND_LATER_BUT_DEPRECATED_IN_WEBKIT_VERSION_3_0); + +@end diff --git a/Source/WebKit/mac/WebView/WebFrameLoadDelegatePrivate.h b/Source/WebKit/mac/WebView/WebFrameLoadDelegatePrivate.h new file mode 100644 index 0000000..2359cc6 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebFrameLoadDelegatePrivate.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009 Adam Barth. All rights reserved. + * + * 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 <WebKit/WebFrameLoadDelegate.h> + +@class WebScriptWorld; +@class WebSecurityOrigin; + +@interface NSObject (WebFrameLoadDelegatePrivate) + +- (void)webViewDidDisplayInsecureContent:(WebView *)webView; + +- (void)webView:(WebView *)webView didRunInsecureContent:(WebSecurityOrigin *)origin; + +- (void)webView:(WebView *)webView didClearWindowObjectForFrame:(WebFrame *)frame inScriptWorld:(WebScriptWorld *)world; + +- (void)webView:(WebView *)webView didPushStateWithinPageForFrame:(WebFrame *)frame; +- (void)webView:(WebView *)webView didReplaceStateWithinPageForFrame:(WebFrame *)frame; +- (void)webView:(WebView *)webView didPopStateWithinPageForFrame:(WebFrame *)frame; + +@end diff --git a/Source/WebKit/mac/WebView/WebFramePrivate.h b/Source/WebKit/mac/WebView/WebFramePrivate.h new file mode 100644 index 0000000..ddfa5cc --- /dev/null +++ b/Source/WebKit/mac/WebView/WebFramePrivate.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Computer, Inc. All rights reserved. + * + * 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. + */ + +// This header contains the WebFrame SPI. + +#import <WebKit/WebFrame.h> +#import <JavaScriptCore/JSBase.h> + +#if !defined(ENABLE_NETSCAPE_PLUGIN_API) +#define ENABLE_NETSCAPE_PLUGIN_API 1 +#endif + +@class DOMDocumentFragment; +@class DOMNode; +@class DOMRange; +@class WebScriptObject; +@class WebScriptWorld; + +// Keys for accessing the values in the page cache dictionary. +extern NSString *WebPageCacheEntryDateKey; +extern NSString *WebPageCacheDataSourceKey; +extern NSString *WebPageCacheDocumentViewKey; + +extern NSString *WebFrameMainDocumentError; +extern NSString *WebFrameHasPlugins; +extern NSString *WebFrameHasUnloadListener; +extern NSString *WebFrameUsesDatabases; +extern NSString *WebFrameUsesGeolocation; +extern NSString *WebFrameUsesApplicationCache; +extern NSString *WebFrameCanSuspendActiveDOMObjects; + +typedef enum { + WebFrameLoadTypeStandard, + WebFrameLoadTypeBack, + WebFrameLoadTypeForward, + WebFrameLoadTypeIndexedBackForward, // a multi-item hop in the backforward list + WebFrameLoadTypeReload, + WebFrameLoadTypeReloadAllowingStaleData, + WebFrameLoadTypeSame, // user loads same URL again (but not reload button) + WebFrameLoadTypeInternal, // maps to WebCore::FrameLoadTypeRedirectWithLockedBackForwardList + WebFrameLoadTypeReplace, + WebFrameLoadTypeReloadFromOrigin, + WebFrameLoadTypeBackWMLDeckNotAccessible +} WebFrameLoadType; + +@interface WebFrame (WebPrivate) +- (BOOL)_isDescendantOfFrame:(WebFrame *)frame; +- (void)_setShouldCreateRenderers:(BOOL)f; +- (NSColor *)_bodyBackgroundColor; +- (BOOL)_isFrameSet; +- (BOOL)_firstLayoutDone; +- (WebFrameLoadType)_loadType; + +// These methods take and return NSRanges based on the root editable element as the positional base. +// This fits with AppKit's idea of an input context. These methods are slow compared to their DOMRange equivalents. +// You should use WebView's selectedDOMRange and setSelectedDOMRange whenever possible. +- (NSRange)_selectedNSRange; +- (void)_selectNSRange:(NSRange)range; + +- (BOOL)_isDisplayingStandaloneImage; + +- (unsigned)_pendingFrameUnloadEventCount; + +- (void)_setIsDisconnected:(bool)isDisconnected; +- (void)_setExcludeFromTextSearch:(bool)exclude; + +#if ENABLE_NETSCAPE_PLUGIN_API +- (void)_recursive_resumeNullEventsForAllNetscapePlugins; +- (void)_recursive_pauseNullEventsForAllNetscapePlugins; +#endif + +- (NSString *)_stringByEvaluatingJavaScriptFromString:(NSString *)string withGlobalObject:(JSObjectRef)globalObject inScriptWorld:(WebScriptWorld *)world; +- (JSGlobalContextRef)_globalContextForScriptWorld:(WebScriptWorld *)world; + +// Pause a given CSS animation or transition on the target node at a specific time. +// If the animation or transition is already paused, it will update its pause time. +// This method is only intended to be used for testing the CSS animation and transition system. +- (BOOL)_pauseAnimation:(NSString*)name onNode:(DOMNode *)node atTime:(NSTimeInterval)time; +- (BOOL)_pauseTransitionOfProperty:(NSString*)name onNode:(DOMNode*)node atTime:(NSTimeInterval)time; + +// Pause a given SVG animation on the target node at a specific time. +// This method is only intended to be used for testing the SVG animation system. +- (BOOL)_pauseSVGAnimation:(NSString*)elementId onSMILNode:(DOMNode *)node atTime:(NSTimeInterval)time; + +// Returns the total number of currently running animations (includes both CSS transitions and CSS animations). +- (unsigned)_numberOfActiveAnimations; + +// Suspend and resume animations (includes both CSS transitions and CSS animations). +- (void)_suspendAnimations; +- (void)_resumeAnimations; + +- (void)_replaceSelectionWithFragment:(DOMDocumentFragment *)fragment selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace matchStyle:(BOOL)matchStyle; +- (void)_replaceSelectionWithText:(NSString *)text selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace; +- (void)_replaceSelectionWithMarkupString:(NSString *)markupString baseURLString:(NSString *)baseURLString selectReplacement:(BOOL)selectReplacement smartReplace:(BOOL)smartReplace; + +- (void)_smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)rangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString; + +- (NSMutableDictionary *)_cacheabilityDictionary; + +- (BOOL)_allowsFollowingLink:(NSURL *)URL; + +// Sets whether the scrollbars, if any, should be shown inside the document's border +// (thus overlapping some content) or outside the webView's border (default behavior). +// Changing this flag changes the size of the contentView and maintains the size of the frameView. +- (void)setAllowsScrollersToOverlapContent:(BOOL)flag; + +// Sets if the scrollbar is always hidden, regardless of other scrollbar visibility settings. +// This does not affect the scrollability of the document. +- (void)setAlwaysHideHorizontalScroller:(BOOL)flag; +- (void)setAlwaysHideVerticalScroller:(BOOL)flag; + +// Sets the name presented to accessibility clients for the web area object. +- (void)setAccessibleName:(NSString *)name; + +- (NSString*)_layerTreeAsText; + +// Returns whether there is a spelling marker in the specified range of the focused node. +- (BOOL)hasSpellingMarker:(int)location length:(int)length; + +// The top of the accessibility tree. +- (id)accessibilityRoot; + +@end diff --git a/Source/WebKit/mac/WebView/WebFrameView.h b/Source/WebKit/mac/WebView/WebFrameView.h new file mode 100644 index 0000000..e170403 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebFrameView.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * + * 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 <Cocoa/Cocoa.h> + +@class WebDataSource; +@class WebFrame; +@class WebFrameViewPrivate; + +@protocol WebDocumentView; + +/*! + @class WebFrameView +*/ +@interface WebFrameView : NSView +{ +@private + WebFrameViewPrivate *_private; +} + +/*! + @method webFrame + @abstract Returns the WebFrame associated with this WebFrameView + @result The WebFrameView's frame. +*/ +- (WebFrame *)webFrame; + +/*! + @method documentView + @abstract Returns the WebFrameView's document subview + @result The subview that renders the WebFrameView's contents +*/ +- (NSView <WebDocumentView> *)documentView; + +/*! + @method setAllowsScrolling: + @abstract Sets whether the WebFrameView allows its document to be scrolled + @param flag YES to allow the document to be scrolled, NO to disallow scrolling +*/ +- (void)setAllowsScrolling:(BOOL)flag; + +/*! + @method allowsScrolling + @abstract Returns whether the WebFrameView allows its document to be scrolled + @result YES if the document is allowed to scroll, otherwise NO +*/ +- (BOOL)allowsScrolling; + +/*! + @method canPrintHeadersAndFooters + @abstract Tells whether this frame can print headers and footers + @result YES if the frame can, no otherwise +*/ +- (BOOL)canPrintHeadersAndFooters; + +/*! + @method printOperationWithPrintInfo + @abstract Creates a print operation set up to print this frame + @result A newly created print operation object +*/ +- (NSPrintOperation *)printOperationWithPrintInfo:(NSPrintInfo *)printInfo; + +/*! + @method documentViewShouldHandlePrint + @abstract Called by the host application before it initializes and runs a print operation. + @result If NO is returned, the host application will abort its print operation and call -printDocumentView on the + WebFrameView. The document view is then expected to run its own print operation. If YES is returned, the host + application's print operation will continue as normal. +*/ +- (BOOL)documentViewShouldHandlePrint; + +/*! + @method printDocumentView + @abstract Called by the host application when the WebFrameView returns YES from -documentViewShouldHandlePrint. +*/ +- (void)printDocumentView; + +@end diff --git a/Source/WebKit/mac/WebView/WebFrameView.mm b/Source/WebKit/mac/WebView/WebFrameView.mm new file mode 100644 index 0000000..801c1f4 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebFrameView.mm @@ -0,0 +1,1130 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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 "WebFrameView.h" + +#import "WebClipView.h" +#import "WebDataSourcePrivate.h" +#import "WebDocument.h" +#import "WebDynamicScrollBarsViewInternal.h" +#import "WebFrame.h" +#import "WebFrameInternal.h" +#import "WebFrameViewInternal.h" +#import "WebFrameViewPrivate.h" +#import "WebHistoryItemInternal.h" +#import "WebHTMLViewPrivate.h" +#import "WebKeyGenerator.h" +#import "WebKitErrorsPrivate.h" +#import "WebKitStatisticsPrivate.h" +#import "WebKitVersionChecks.h" +#import "WebNSDictionaryExtras.h" +#import "WebNSObjectExtras.h" +#import "WebNSPasteboardExtras.h" +#import "WebNSViewExtras.h" +#import "WebNSWindowExtras.h" +#import "WebPDFView.h" +#import "WebPreferenceKeysPrivate.h" +#import "WebResourceInternal.h" +#import "WebSystemInterface.h" +#import "WebViewFactory.h" +#import "WebViewInternal.h" +#import "WebViewPrivate.h" +#import <Foundation/NSURLRequest.h> +#import <WebCore/BackForwardListImpl.h> +#import <WebCore/DragController.h> +#import <WebCore/EventHandler.h> +#import <WebCore/Frame.h> +#import <WebCore/FrameView.h> +#import <WebCore/HistoryItem.h> +#import <WebCore/Page.h> +#import <WebCore/RenderPart.h> +#import <WebCore/ThreadCheck.h> +#import <WebCore/WebCoreFrameView.h> +#import <WebCore/WebCoreView.h> +#import <WebKitSystemInterface.h> +#import <wtf/Assertions.h> + +using namespace WebCore; + +@interface NSWindow (WindowPrivate) +- (BOOL)_needsToResetDragMargins; +- (void)_setNeedsToResetDragMargins:(BOOL)s; +@end + +@interface NSClipView (AppKitSecretsIKnow) +- (BOOL)_scrollTo:(const NSPoint *)newOrigin animate:(BOOL)animate; // need the boolean result from this method +@end + +enum { + SpaceKey = 0x0020 +}; + +@interface WebFrameView (WebFrameViewFileInternal) <WebCoreFrameView> +- (float)_verticalKeyboardScrollDistance; +@end + +@interface WebFrameViewPrivate : NSObject { +@public + WebFrame *webFrame; + WebDynamicScrollBarsView *frameScrollView; + BOOL includedInWebKitStatistics; +} +@end + +@implementation WebFrameViewPrivate + +- (void)dealloc +{ + [frameScrollView release]; + [super dealloc]; +} + +@end + +@implementation WebFrameView (WebFrameViewFileInternal) + +- (float)_verticalKeyboardScrollDistance +{ + // Arrow keys scroll the same distance that clicking the scroll arrow does. + return [[self _scrollView] verticalLineScroll]; +} + +- (Frame*)_web_frame +{ + return core(_private->webFrame); +} + +@end + +@implementation WebFrameView (WebInternal) + +// Note that the WebVew is not retained. +- (WebView *)_webView +{ + return [_private->webFrame webView]; +} + +- (void)_setDocumentView:(NSView <WebDocumentView> *)view +{ + WebDynamicScrollBarsView *sv = [self _scrollView]; + core([self _webView])->dragController()->setDidInitiateDrag(false); + + [sv setSuppressLayout:YES]; + + // If the old view is the first responder, transfer first responder status to the new view as + // a convenience and so that we don't leave the window pointing to a view that's no longer in it. + NSWindow *window = [sv window]; + NSResponder *firstResponder = [window firstResponder]; + bool makeNewViewFirstResponder = [firstResponder isKindOfClass:[NSView class]] && [(NSView *)firstResponder isDescendantOf:[sv documentView]]; + + // Suppress the resetting of drag margins since we know we can't affect them. + BOOL resetDragMargins = [window _needsToResetDragMargins]; + [window _setNeedsToResetDragMargins:NO]; + [sv setDocumentView:view]; + [window _setNeedsToResetDragMargins:resetDragMargins]; + + if (makeNewViewFirstResponder) + [window makeFirstResponder:view]; + [sv setSuppressLayout:NO]; +} + +-(NSView <WebDocumentView> *)_makeDocumentViewForDataSource:(WebDataSource *)dataSource +{ + NSString* MIMEType = [dataSource _responseMIMEType]; + if (!MIMEType) + MIMEType = @"text/html"; + Class viewClass = [self _viewClassForMIMEType:MIMEType]; + NSView <WebDocumentView> *documentView; + if (viewClass) { + // If the dataSource's representation has already been created, and it is also the + // same class as the desired documentView, then use it as the documentView instead + // of creating another one (Radar 4340787). + id <WebDocumentRepresentation> dataSourceRepresentation = [dataSource representation]; + if (dataSourceRepresentation && [dataSourceRepresentation class] == viewClass) + documentView = (NSView <WebDocumentView> *)[dataSourceRepresentation retain]; + else + documentView = [[viewClass alloc] initWithFrame:[self bounds]]; + } else + documentView = nil; + + [self _setDocumentView:documentView]; + [documentView release]; + + return documentView; +} + +- (void)_setWebFrame:(WebFrame *)webFrame +{ + if (!webFrame) { + NSView *docV = [self documentView]; + if ([docV respondsToSelector:@selector(close)]) + [docV performSelector:@selector(close)]; + } + + // Not retained because the WebView owns the WebFrame, which owns the WebFrameView. + _private->webFrame = webFrame; + + if (!_private->includedInWebKitStatistics && [webFrame _isIncludedInWebKitStatistics]) { + _private->includedInWebKitStatistics = YES; + ++WebFrameViewCount; + } +} + +- (WebDynamicScrollBarsView *)_scrollView +{ + // This can be called by [super dealloc] when cleaning up the key view loop, + // after _private has been nilled out. + if (_private == nil) + return nil; + return _private->frameScrollView; +} + +- (float)_verticalPageScrollDistance +{ + float height = [[self _contentView] bounds].size.height; + return max<float>(height * Scrollbar::minFractionToStepWhenPaging(), height - Scrollbar::maxOverlapBetweenPages()); +} + +static inline void addTypesFromClass(NSMutableDictionary *allTypes, Class objCClass, NSArray *supportTypes) +{ + NSEnumerator *enumerator = [supportTypes objectEnumerator]; + ASSERT(enumerator != nil); + NSString *mime = nil; + while ((mime = [enumerator nextObject]) != nil) { + // Don't clobber previously-registered classes. + if ([allTypes objectForKey:mime] == nil) + [allTypes setObject:objCClass forKey:mime]; + } +} + ++ (NSMutableDictionary *)_viewTypesAllowImageTypeOmission:(BOOL)allowImageTypeOmission +{ + static NSMutableDictionary *viewTypes = nil; + static BOOL addedImageTypes = NO; + + if (!viewTypes) { + viewTypes = [[NSMutableDictionary alloc] init]; + addTypesFromClass(viewTypes, [WebHTMLView class], [WebHTMLView supportedNonImageMIMETypes]); + + // Since this is a "secret default" we don't bother registering it. + BOOL omitPDFSupport = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitOmitPDFSupport"]; + if (!omitPDFSupport) + addTypesFromClass(viewTypes, [WebPDFView class], [WebPDFView supportedMIMETypes]); + } + + if (!addedImageTypes && !allowImageTypeOmission) { + addTypesFromClass(viewTypes, [WebHTMLView class], [WebHTMLView supportedImageMIMETypes]); + addedImageTypes = YES; + } + + return viewTypes; +} + ++ (BOOL)_canShowMIMETypeAsHTML:(NSString *)MIMEType +{ + return [[[self _viewTypesAllowImageTypeOmission:YES] _webkit_objectForMIMEType:MIMEType] isSubclassOfClass:[WebHTMLView class]]; +} + ++ (Class)_viewClassForMIMEType:(NSString *)MIMEType allowingPlugins:(BOOL)allowPlugins +{ + Class viewClass; + return [WebView _viewClass:&viewClass andRepresentationClass:nil forMIMEType:MIMEType allowingPlugins:allowPlugins] ? viewClass : nil; +} + +- (Class)_viewClassForMIMEType:(NSString *)MIMEType +{ + return [[self class] _viewClassForMIMEType:MIMEType allowingPlugins:[[[self _webView] preferences] arePlugInsEnabled]]; +} + +- (void)_install +{ + ASSERT(_private->webFrame); + ASSERT(_private->frameScrollView); + + Frame* frame = core(_private->webFrame); + + ASSERT(frame); + ASSERT(frame->page()); + + // If this isn't the main frame, it must have an owner element set, or it + // won't ever get installed in the view hierarchy. + ASSERT(frame == frame->page()->mainFrame() || frame->ownerElement()); + + FrameView* view = frame->view(); + + view->setPlatformWidget(_private->frameScrollView); + + // FIXME: Frame tries to do this too. Is this code needed? + if (RenderPart* owner = frame->ownerRenderer()) { + owner->setWidget(view); + // Now the render part owns the view, so we don't any more. + } + + view->updateCanHaveScrollbars(); +} + +@end + +@implementation WebFrameView + +- (id)initWithFrame:(NSRect)frame +{ + self = [super initWithFrame:frame]; + if (!self) + return nil; + + static bool didFirstTimeInitialization; + if (!didFirstTimeInitialization) { + didFirstTimeInitialization = true; + InitWebCoreSystemInterface(); + + // Need to tell WebCore what function to call for the "History Item has Changed" notification. + // Note: We also do this in WebHistoryItem's init method. + WebCore::notifyHistoryItemChanged = WKNotifyHistoryItemChanged; + + [WebViewFactory createSharedFactory]; + [WebKeyGenerator createSharedGenerator]; + +// FIXME: Remove the NSAppKitVersionNumberWithDeferredWindowDisplaySupport check once +// once AppKit's Deferred Window Display support is available. +#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) || !defined(NSAppKitVersionNumberWithDeferredWindowDisplaySupport) + // CoreGraphics deferred updates are disabled if WebKitEnableCoalescedUpdatesPreferenceKey is NO + // or has no value. For compatibility with Mac OS X 10.5 and lower, deferred updates are off by default. + if (![[NSUserDefaults standardUserDefaults] boolForKey:WebKitEnableDeferredUpdatesPreferenceKey]) + WKDisableCGDeferredUpdates(); +#endif + if (!WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_MAIN_THREAD_EXCEPTIONS)) + setDefaultThreadViolationBehavior(LogOnFirstThreadViolation, ThreadViolationRoundOne); + + bool throwExceptionsForRoundTwo = WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_ROUND_TWO_MAIN_THREAD_EXCEPTIONS); +#ifdef MAIL_THREAD_WORKAROUND + // Even if old Mail is linked with new WebKit, don't throw exceptions. + if ([WebResource _needMailThreadWorkaroundIfCalledOffMainThread]) + throwExceptionsForRoundTwo = false; +#endif + if (!throwExceptionsForRoundTwo) + setDefaultThreadViolationBehavior(LogOnFirstThreadViolation, ThreadViolationRoundTwo); + } + + _private = [[WebFrameViewPrivate alloc] init]; + + WebDynamicScrollBarsView *scrollView = [[WebDynamicScrollBarsView alloc] initWithFrame:NSMakeRect(0.0f, 0.0f, frame.size.width, frame.size.height)]; + _private->frameScrollView = scrollView; + [scrollView setContentView:[[[WebClipView alloc] initWithFrame:[scrollView bounds]] autorelease]]; + [scrollView setDrawsBackground:NO]; + [scrollView setHasVerticalScroller:NO]; + [scrollView setHasHorizontalScroller:NO]; + [scrollView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [scrollView setLineScroll:Scrollbar::pixelsPerLineStep()]; + [self addSubview:scrollView]; + + // Don't call our overridden version of setNextKeyView here; we need to make the standard NSView + // link between us and our subview so that previousKeyView and previousValidKeyView work as expected. + // This works together with our becomeFirstResponder and setNextKeyView overrides. + [super setNextKeyView:scrollView]; + + return self; +} + +- (void)dealloc +{ + if (_private && _private->includedInWebKitStatistics) + --WebFrameViewCount; + + [_private release]; + _private = nil; + + [super dealloc]; +} + +- (void)finalize +{ + if (_private && _private->includedInWebKitStatistics) + --WebFrameViewCount; + + [super finalize]; +} + +- (WebFrame *)webFrame +{ + // This method can be called beneath -[NSView dealloc] after _private has been cleared. + return _private ? _private->webFrame : nil; +} + +- (void)setAllowsScrolling:(BOOL)flag +{ + WebCore::Frame *frame = core([self webFrame]); + if (WebCore::FrameView *view = frame? frame->view() : 0) + view->setCanHaveScrollbars(flag); +} + +- (BOOL)allowsScrolling +{ + WebCore::Frame *frame = core([self webFrame]); + if (WebCore::FrameView *view = frame? frame->view() : 0) + return view->canHaveScrollbars(); + return YES; +} + +- (NSView <WebDocumentView> *)documentView +{ + return [[self _scrollView] documentView]; +} + +- (BOOL)acceptsFirstResponder +{ + // We always accept first responder; this matches OS X 10.2 WebKit + // behavior (see 3469791). + return YES; +} + +- (BOOL)becomeFirstResponder +{ + // This works together with setNextKeyView to splice the WebFrameView into + // the key loop similar to the way NSScrollView does this. Note that + // WebView has similar code. + + NSWindow *window = [self window]; + if ([window keyViewSelectionDirection] == NSSelectingPrevious) { + NSView *previousValidKeyView = [self previousValidKeyView]; + // If we couldn't find a previous valid key view, ask the WebView. This handles frameset + // cases (one is mentioned in Radar bug 3748628). Note that previousValidKeyView should + // never be self but can be due to AppKit oddness (mentioned in Radar bug 3748628). + if (previousValidKeyView == nil || previousValidKeyView == self) + previousValidKeyView = [[[self webFrame] webView] previousValidKeyView]; + [window makeFirstResponder:previousValidKeyView]; + } else { + // If the scroll view won't accept first-responderness now, then just become + // the first responder ourself like a normal view. This lets us be the first + // responder in cases where no page has yet been loaded. + if ([[self _scrollView] acceptsFirstResponder]) + [window makeFirstResponder:[self _scrollView]]; + } + + return YES; +} + +- (void)setNextKeyView:(NSView *)aView +{ + // This works together with becomeFirstResponder to splice the WebFrameView into + // the key loop similar to the way NSScrollView does this. Note that + // WebView has very similar code. + if ([self _scrollView] != nil) { + [[self _scrollView] setNextKeyView:aView]; + } else { + [super setNextKeyView:aView]; + } +} + +- (BOOL)isOpaque +{ + return [[self _webView] drawsBackground]; +} + +- (void)drawRect:(NSRect)rect +{ + if ([self documentView] == nil) { + // Need to paint ourselves if there's no documentView to do it instead. + if ([[self _webView] drawsBackground]) { + [[[self _webView] backgroundColor] set]; + NSRectFill(rect); + } + } else { +#ifndef NDEBUG + if ([[self _scrollView] drawsBackground]) { + [[NSColor cyanColor] set]; + NSRectFill(rect); + } +#endif + } +} + +- (NSRect)visibleRect +{ + // This method can be called beneath -[NSView dealloc] after we have cleared _private. + if (!_private) + return [super visibleRect]; + + // FIXME: <rdar://problem/6213380> This method does not work correctly with transforms, for two reasons: + // 1) [super visibleRect] does not account for the transform, since it is not represented + // in the NSView hierarchy. + // 2) -_getVisibleRect: does not correct for transforms. + + NSRect rendererVisibleRect; + if (![[self webFrame] _getVisibleRect:&rendererVisibleRect]) + return [super visibleRect]; + + if (NSIsEmptyRect(rendererVisibleRect)) + return NSZeroRect; + + NSRect viewVisibleRect = [super visibleRect]; + if (NSIsEmptyRect(viewVisibleRect)) + return NSZeroRect; + + NSRect frame = [self frame]; + // rendererVisibleRect is in the parent's coordinate space, and frame is in the superview's coordinate space. + // The return value from this method needs to be in this view's coordinate space. We get that right by subtracting + // the origins (and correcting for flipping), but when we support transforms, we will need to do better than this. + rendererVisibleRect.origin.x -= frame.origin.x; + rendererVisibleRect.origin.y = NSMaxY(frame) - NSMaxY(rendererVisibleRect); + return NSIntersectionRect(rendererVisibleRect, viewVisibleRect); +} + +- (void)setFrameSize:(NSSize)size +{ + if (!NSEqualSizes(size, [self frame].size)) { + // See WebFrameLoaderClient::provisionalLoadStarted. + if ([[[self webFrame] webView] drawsBackground]) + [[self _scrollView] setDrawsBackground:YES]; + if (Frame* coreFrame = [self _web_frame]) { + if (FrameView* coreFrameView = coreFrame->view()) + coreFrameView->setNeedsLayout(); + } + } + [super setFrameSize:size]; +} + +- (void)setBoundsSize:(NSSize)size +{ + [super setBoundsSize:size]; + [[self _scrollView] setFrameSize:size]; +} + +- (void)viewDidMoveToWindow +{ + // See WebFrameLoaderClient::provisionalLoadStarted. + // Need to check _private for nil because this can be called inside -[WebView initWithCoder:]. + if (_private && [[[self webFrame] webView] drawsBackground]) + [[self _scrollView] setDrawsBackground:YES]; + [super viewDidMoveToWindow]; +} + +- (BOOL)_scrollOverflowInDirection:(ScrollDirection)direction granularity:(ScrollGranularity)granularity +{ + // scrolling overflows is only applicable if we're dealing with an WebHTMLView + if (![[self documentView] isKindOfClass:[WebHTMLView class]]) + return NO; + Frame* frame = core([self webFrame]); + if (!frame) + return NO; + return frame->eventHandler()->scrollOverflow(direction, granularity); +} + + +- (BOOL)_isVerticalDocument +{ + Frame* coreFrame = [self _web_frame]; + if (!coreFrame) + return YES; + Document* document = coreFrame->document(); + if (!document) + return YES; + RenderObject* renderView = document->renderer(); + if (!renderView) + return YES; + return renderView->style()->isHorizontalWritingMode(); +} + +- (BOOL)_isFlippedDocument +{ + Frame* coreFrame = [self _web_frame]; + if (!coreFrame) + return NO; + Document* document = coreFrame->document(); + if (!document) + return NO; + RenderObject* renderView = document->renderer(); + if (!renderView) + return NO; + return renderView->style()->isFlippedBlocksWritingMode(); +} + +- (BOOL)_scrollToBeginningOfDocument +{ + if ([self _scrollOverflowInDirection:ScrollUp granularity:ScrollByDocument]) + return YES; + if (![self _isScrollable]) + return NO; + NSPoint point = [[[self _scrollView] documentView] frame].origin; + point.x += [[self _scrollView] scrollOrigin].x; + point.y += [[self _scrollView] scrollOrigin].y; + return [[self _contentView] _scrollTo:&point animate:YES]; +} + +- (BOOL)_scrollToEndOfDocument +{ + if ([self _scrollOverflowInDirection:ScrollDown granularity:ScrollByDocument]) + return YES; + if (![self _isScrollable]) + return NO; + NSRect frame = [[[self _scrollView] documentView] frame]; + + bool isVertical = [self _isVerticalDocument]; + bool isFlipped = [self _isFlippedDocument]; + + NSPoint point; + if (isVertical) { + if (!isFlipped) + point = NSMakePoint(frame.origin.x, NSMaxY(frame)); + else + point = NSMakePoint(frame.origin.x, NSMinY(frame)); + } else { + if (!isFlipped) + point = NSMakePoint(NSMaxX(frame), frame.origin.y); + else + point = NSMakePoint(NSMinX(frame), frame.origin.y); + } + + // Reset the position opposite to the block progression direction. + if (isVertical) + point.x += [[self _scrollView] scrollOrigin].x; + else + point.y += [[self _scrollView] scrollOrigin].y; + return [[self _contentView] _scrollTo:&point animate:YES]; +} + +- (void)scrollToBeginningOfDocument:(id)sender +{ + if ([self _scrollToBeginningOfDocument]) + return; + + if (WebFrameView *child = [self _largestScrollableChild]) { + if ([child _scrollToBeginningOfDocument]) + return; + } + [[self nextResponder] tryToPerform:@selector(scrollToBeginningOfDocument:) with:sender]; +} + +- (void)scrollToEndOfDocument:(id)sender +{ + if ([self _scrollToEndOfDocument]) + return; + + if (WebFrameView *child = [self _largestScrollableChild]) { + if ([child _scrollToEndOfDocument]) + return; + } + [[self nextResponder] tryToPerform:@selector(scrollToEndOfDocument:) with:sender]; +} + +- (void)_goBack +{ + [[self _webView] goBack]; +} + +- (void)_goForward +{ + [[self _webView] goForward]; +} + +- (BOOL)_scrollVerticallyBy:(float)delta +{ + // This method uses the secret method _scrollTo on NSClipView. + // It does that because it needs to know definitively whether scrolling was + // done or not to help implement the "scroll parent if we are at the limit" feature. + // In the presence of smooth scrolling, there's no easy way to tell if the method + // did any scrolling or not with the public API. + NSPoint point = [[self _contentView] bounds].origin; + point.y += delta; + return [[self _contentView] _scrollTo:&point animate:YES]; +} + +- (BOOL)_scrollHorizontallyBy:(float)delta +{ + NSPoint point = [[self _contentView] bounds].origin; + point.x += delta; + return [[self _contentView] _scrollTo:&point animate:YES]; +} + +- (float)_horizontalKeyboardScrollDistance +{ + // Arrow keys scroll the same distance that clicking the scroll arrow does. + return [[self _scrollView] horizontalLineScroll]; +} + +- (float)_horizontalPageScrollDistance +{ + float width = [[self _contentView] bounds].size.width; + return max<float>(width * Scrollbar::minFractionToStepWhenPaging(), width - Scrollbar::maxOverlapBetweenPages()); +} + +- (BOOL)_pageVertically:(BOOL)up +{ + if ([self _scrollOverflowInDirection:up ? ScrollUp : ScrollDown granularity:ScrollByPage]) + return YES; + + if (![self _isScrollable]) + return [[self _largestScrollableChild] _pageVertically:up]; + + float delta = [self _verticalPageScrollDistance]; + return [self _scrollVerticallyBy:up ? -delta : delta]; +} + +- (BOOL)_pageHorizontally:(BOOL)left +{ + if ([self _scrollOverflowInDirection:left ? ScrollLeft : ScrollRight granularity:ScrollByPage]) + return YES; + + if (![self _isScrollable]) + return [[self _largestScrollableChild] _pageHorizontally:left]; + + float delta = [self _horizontalPageScrollDistance]; + return [self _scrollHorizontallyBy:left ? -delta : delta]; +} + +- (BOOL)_pageInBlockProgressionDirection:(BOOL)forward +{ + // Determine whether we're calling _pageVertically or _pageHorizontally. + BOOL isVerticalDocument = [self _isVerticalDocument]; + BOOL isFlippedBlock = [self _isFlippedDocument]; + if (isVerticalDocument) + return [self _pageVertically:isFlippedBlock ? !forward : forward]; + return [self _pageHorizontally:isFlippedBlock ? !forward : forward]; +} + +- (BOOL)_scrollLineVertically:(BOOL)up +{ + if ([self _scrollOverflowInDirection:up ? ScrollUp : ScrollDown granularity:ScrollByLine]) + return YES; + + if (![self _isScrollable]) + return [[self _largestScrollableChild] _scrollLineVertically:up]; + + float delta = [self _verticalKeyboardScrollDistance]; + return [self _scrollVerticallyBy:up ? -delta : delta]; +} + +- (BOOL)_scrollLineHorizontally:(BOOL)left +{ + if ([self _scrollOverflowInDirection:left ? ScrollLeft : ScrollRight granularity:ScrollByLine]) + return YES; + + if (![self _isScrollable]) + return [[self _largestScrollableChild] _scrollLineHorizontally:left]; + + float delta = [self _horizontalKeyboardScrollDistance]; + return [self _scrollHorizontallyBy:left ? -delta : delta]; +} + +- (void)scrollPageUp:(id)sender +{ + if (![self _pageInBlockProgressionDirection:YES]) { + // If we were already at the top, tell the next responder to scroll if it can. + [[self nextResponder] tryToPerform:@selector(scrollPageUp:) with:sender]; + } +} + +- (void)scrollPageDown:(id)sender +{ + if (![self _pageInBlockProgressionDirection:NO]) { + // If we were already at the bottom, tell the next responder to scroll if it can. + [[self nextResponder] tryToPerform:@selector(scrollPageDown:) with:sender]; + } +} + +- (void)scrollLineUp:(id)sender +{ + if (![self _scrollLineVertically:YES]) + [[self nextResponder] tryToPerform:@selector(scrollLineUp:) with:sender]; +} + +- (void)scrollLineDown:(id)sender +{ + if (![self _scrollLineVertically:NO]) + [[self nextResponder] tryToPerform:@selector(scrollLineDown:) with:sender]; +} + +- (BOOL)_firstResponderIsFormControl +{ + NSResponder *firstResponder = [[self window] firstResponder]; + + // WebHTMLView is an NSControl subclass these days, but it's not a form control + if ([firstResponder isKindOfClass:[WebHTMLView class]]) { + return NO; + } + return [firstResponder isKindOfClass:[NSControl class]]; +} + +- (void)keyDown:(NSEvent *)event +{ + NSString *characters = [event characters]; + int index, count; + BOOL callSuper = YES; + Frame* coreFrame = [self _web_frame]; + BOOL maintainsBackForwardList = coreFrame && static_cast<BackForwardListImpl*>(coreFrame->page()->backForwardList())->enabled() ? YES : NO; + + count = [characters length]; + for (index = 0; index < count; ++index) { + switch ([characters characterAtIndex:index]) { + case NSDeleteCharacter: + if (!maintainsBackForwardList) { + callSuper = YES; + break; + } + // This odd behavior matches some existing browsers, + // including Windows IE + if ([event modifierFlags] & NSShiftKeyMask) { + [self _goForward]; + } else { + [self _goBack]; + } + callSuper = NO; + break; + case SpaceKey: + // Checking for a control will allow events to percolate + // correctly when the focus is on a form control and we + // are in full keyboard access mode. + if ((![self allowsScrolling] && ![self _largestScrollableChild]) || [self _firstResponderIsFormControl]) { + callSuper = YES; + break; + } + if ([event modifierFlags] & NSShiftKeyMask) { + [self scrollPageUp:nil]; + } else { + [self scrollPageDown:nil]; + } + callSuper = NO; + break; + case NSPageUpFunctionKey: + if (![self allowsScrolling] && ![self _largestScrollableChild]) { + callSuper = YES; + break; + } + [self scrollPageUp:nil]; + callSuper = NO; + break; + case NSPageDownFunctionKey: + if (![self allowsScrolling] && ![self _largestScrollableChild]) { + callSuper = YES; + break; + } + [self scrollPageDown:nil]; + callSuper = NO; + break; + case NSHomeFunctionKey: + if (![self allowsScrolling] && ![self _largestScrollableChild]) { + callSuper = YES; + break; + } + [self scrollToBeginningOfDocument:nil]; + callSuper = NO; + break; + case NSEndFunctionKey: + if (![self allowsScrolling] && ![self _largestScrollableChild]) { + callSuper = YES; + break; + } + [self scrollToEndOfDocument:nil]; + callSuper = NO; + break; + case NSUpArrowFunctionKey: + // We don't handle shifted or control-arrow keys here, so let super have a chance. + if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) { + callSuper = YES; + break; + } + if ((![self allowsScrolling] && ![self _largestScrollableChild]) || + [[[self window] firstResponder] isKindOfClass:[NSPopUpButton class]]) { + // Let arrow keys go through to pop up buttons + // <rdar://problem/3455910>: hitting up or down arrows when focus is on a + // pop-up menu should pop the menu + callSuper = YES; + break; + } + if ([event modifierFlags] & NSCommandKeyMask) { + [self scrollToBeginningOfDocument:nil]; + } else if ([event modifierFlags] & NSAlternateKeyMask) { + [self scrollPageUp:nil]; + } else { + [self scrollLineUp:nil]; + } + callSuper = NO; + break; + case NSDownArrowFunctionKey: + // We don't handle shifted or control-arrow keys here, so let super have a chance. + if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) { + callSuper = YES; + break; + } + if ((![self allowsScrolling] && ![self _largestScrollableChild]) || + [[[self window] firstResponder] isKindOfClass:[NSPopUpButton class]]) { + // Let arrow keys go through to pop up buttons + // <rdar://problem/3455910>: hitting up or down arrows when focus is on a + // pop-up menu should pop the menu + callSuper = YES; + break; + } + if ([event modifierFlags] & NSCommandKeyMask) { + [self scrollToEndOfDocument:nil]; + } else if ([event modifierFlags] & NSAlternateKeyMask) { + [self scrollPageDown:nil]; + } else { + [self scrollLineDown:nil]; + } + callSuper = NO; + break; + case NSLeftArrowFunctionKey: + // We don't handle shifted or control-arrow keys here, so let super have a chance. + if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) { + callSuper = YES; + break; + } + // Check back/forward related keys. + if ([event modifierFlags] & NSCommandKeyMask) { + if (!maintainsBackForwardList) { + callSuper = YES; + break; + } + [self _goBack]; + } else { + // Now check scrolling related keys. + if ((![self allowsScrolling] && ![self _largestScrollableChild])) { + callSuper = YES; + break; + } + + if ([event modifierFlags] & NSAlternateKeyMask) { + [self _pageHorizontally:YES]; + } else { + [self _scrollLineHorizontally:YES]; + } + } + callSuper = NO; + break; + case NSRightArrowFunctionKey: + // We don't handle shifted or control-arrow keys here, so let super have a chance. + if ([event modifierFlags] & (NSShiftKeyMask | NSControlKeyMask)) { + callSuper = YES; + break; + } + // Check back/forward related keys. + if ([event modifierFlags] & NSCommandKeyMask) { + if (!maintainsBackForwardList) { + callSuper = YES; + break; + } + [self _goForward]; + } else { + // Now check scrolling related keys. + if ((![self allowsScrolling] && ![self _largestScrollableChild])) { + callSuper = YES; + break; + } + + if ([event modifierFlags] & NSAlternateKeyMask) { + [self _pageHorizontally:NO]; + } else { + [self _scrollLineHorizontally:NO]; + } + } + callSuper = NO; + break; + } + } + + if (callSuper) { + [super keyDown:event]; + } else { + // if we did something useful, get the cursor out of the way + [NSCursor setHiddenUntilMouseMoves:YES]; + } +} + +- (NSView *)_webcore_effectiveFirstResponder +{ + NSView *view = [self documentView]; + return view ? [view _webcore_effectiveFirstResponder] : [super _webcore_effectiveFirstResponder]; +} + +- (BOOL)canPrintHeadersAndFooters +{ + NSView *documentView = [[self _scrollView] documentView]; + if ([documentView respondsToSelector:@selector(canPrintHeadersAndFooters)]) { + return [(id)documentView canPrintHeadersAndFooters]; + } + return NO; +} + +- (NSPrintOperation *)printOperationWithPrintInfo:(NSPrintInfo *)printInfo +{ + NSView *documentView = [[self _scrollView] documentView]; + if (!documentView) { + return nil; + } + if ([documentView respondsToSelector:@selector(printOperationWithPrintInfo:)]) { + return [(id)documentView printOperationWithPrintInfo:printInfo]; + } + return [NSPrintOperation printOperationWithView:documentView printInfo:printInfo]; +} + +- (BOOL)documentViewShouldHandlePrint +{ + NSView *documentView = [[self _scrollView] documentView]; + if (documentView && [documentView respondsToSelector:@selector(documentViewShouldHandlePrint)]) + return [(id)documentView documentViewShouldHandlePrint]; + + return NO; +} + +- (void)printDocumentView +{ + NSView *documentView = [[self _scrollView] documentView]; + if (documentView && [documentView respondsToSelector:@selector(printDocumentView)]) + [(id)documentView printDocumentView]; +} + +@end + +@implementation WebFrameView (WebPrivate) + +- (float)_area +{ + NSRect frame = [self frame]; + return frame.size.height * frame.size.width; +} + +- (BOOL)_isScrollable +{ + WebDynamicScrollBarsView *scrollView = [self _scrollView]; + return [scrollView horizontalScrollingAllowed] || [scrollView verticalScrollingAllowed]; +} + +- (WebFrameView *)_largestScrollableChild +{ + WebFrameView *largest = nil; + NSArray *frameChildren = [[self webFrame] childFrames]; + + unsigned i; + for (i=0; i < [frameChildren count]; i++) { + WebFrameView *childFrameView = [[frameChildren objectAtIndex:i] frameView]; + WebFrameView *scrollableFrameView = [childFrameView _isScrollable] ? childFrameView : [childFrameView _largestScrollableChild]; + if (!scrollableFrameView) + continue; + + // Some ads lurk in child frames of zero width and height, see radar 4406994. These don't count as scrollable. + // Maybe someday we'll discover that this minimum area check should be larger, but this covers the known cases. + float area = [scrollableFrameView _area]; + if (area < 1.0) + continue; + + if (!largest || (area > [largest _area])) { + largest = scrollableFrameView; + } + } + + return largest; +} + +- (BOOL)_hasScrollBars +{ + // FIXME: This method was used by Safari 4.0.x and older versions, but has not been used by any other WebKit + // clients to my knowledge, and will not be used by future versions of Safari. It can probably be removed + // once we no longer need to keep nightly WebKit builds working with Safari 4.0.x and earlier. + NSScrollView *scrollView = [self _scrollView]; + return [scrollView hasHorizontalScroller] || [scrollView hasVerticalScroller]; +} + +- (WebFrameView *)_largestChildWithScrollBars +{ + // FIXME: This method was used by Safari 4.0.x and older versions, but has not been used by any other WebKit + // clients to my knowledge, and will not be used by future versions of Safari. It can probably be removed + // once we no longer need to keep nightly WebKit builds working with Safari 4.0.x and earlier. + WebFrameView *largest = nil; + NSArray *frameChildren = [[self webFrame] childFrames]; + + unsigned i; + for (i=0; i < [frameChildren count]; i++) { + WebFrameView *childFrameView = [[frameChildren objectAtIndex:i] frameView]; + WebFrameView *scrollableFrameView = [childFrameView _hasScrollBars] ? childFrameView : [childFrameView _largestChildWithScrollBars]; + if (!scrollableFrameView) + continue; + + // Some ads lurk in child frames of zero width and height, see radar 4406994. These don't count as scrollable. + // Maybe someday we'll discover that this minimum area check should be larger, but this covers the known cases. + float area = [scrollableFrameView _area]; + if (area < 1.0) + continue; + + if (!largest || (area > [largest _area])) { + largest = scrollableFrameView; + } + } + + return largest; +} + +- (NSClipView *)_contentView +{ + return [[self _scrollView] contentView]; +} + +- (Class)_customScrollViewClass +{ + if ([_private->frameScrollView class] == [WebDynamicScrollBarsView class]) + return nil; + return [_private->frameScrollView class]; +} + +- (void)_setCustomScrollViewClass:(Class)customClass +{ + if (!customClass) + customClass = [WebDynamicScrollBarsView class]; + ASSERT([customClass isSubclassOfClass:[WebDynamicScrollBarsView class]]); + if (customClass == [_private->frameScrollView class]) + return; + if (![customClass isSubclassOfClass:[WebDynamicScrollBarsView class]]) + return; + + WebDynamicScrollBarsView *oldScrollView = _private->frameScrollView; // already retained + NSView <WebDocumentView> *documentView = [[self documentView] retain]; + + WebDynamicScrollBarsView *scrollView = [[customClass alloc] initWithFrame:[oldScrollView frame]]; + [scrollView setContentView:[[[WebClipView alloc] initWithFrame:[scrollView bounds]] autorelease]]; + [scrollView setDrawsBackground:[oldScrollView drawsBackground]]; + [scrollView setHasVerticalScroller:[oldScrollView hasVerticalScroller]]; + [scrollView setHasHorizontalScroller:[oldScrollView hasHorizontalScroller]]; + [scrollView setAutoresizingMask:[oldScrollView autoresizingMask]]; + [scrollView setLineScroll:[oldScrollView lineScroll]]; + [self addSubview:scrollView]; + + // don't call our overridden version here; we need to make the standard NSView link between us + // and our subview so that previousKeyView and previousValidKeyView work as expected. This works + // together with our becomeFirstResponder and setNextKeyView overrides. + [super setNextKeyView:scrollView]; + + _private->frameScrollView = scrollView; + + [self _setDocumentView:documentView]; + [self _install]; + + [oldScrollView removeFromSuperview]; + [oldScrollView release]; + [documentView release]; +} + +@end diff --git a/Source/WebKit/mac/WebView/WebFrameViewInternal.h b/Source/WebKit/mac/WebView/WebFrameViewInternal.h new file mode 100644 index 0000000..9d7fd80 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebFrameViewInternal.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * 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 <WebKit/WebFrameView.h> + +@class WebDynamicScrollBarsView; +@class WebView; + +@interface WebFrameView (WebInternal) + +- (WebView *)_webView; +- (void)_setDocumentView:(NSView <WebDocumentView> *)view; +- (NSView <WebDocumentView> *)_makeDocumentViewForDataSource:(WebDataSource *)dataSource; +- (void)_setWebFrame:(WebFrame *)webFrame; +- (float)_verticalPageScrollDistance; ++ (NSMutableDictionary *)_viewTypesAllowImageTypeOmission:(BOOL)allowImageTypeOmission; +- (Class)_viewClassForMIMEType:(NSString *)MIMEType; ++ (Class)_viewClassForMIMEType:(NSString *)MIMEType allowingPlugins:(BOOL)allowPlugins; ++ (BOOL)_canShowMIMETypeAsHTML:(NSString *)MIMEType; +- (WebDynamicScrollBarsView *)_scrollView; +- (void)_install; + +@end diff --git a/Source/WebKit/mac/WebView/WebFrameViewPrivate.h b/Source/WebKit/mac/WebView/WebFrameViewPrivate.h new file mode 100644 index 0000000..93d36ec --- /dev/null +++ b/Source/WebKit/mac/WebView/WebFrameViewPrivate.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * 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 <WebKit/WebFrameView.h> + +@interface WebFrameView (WebPrivate) + +// FIXME: This method was used by Safari 4.0.x and older versions, but has not been used by any other WebKit +// clients to my knowledge, and will not be used by future versions of Safari. It can probably be removed +// once we no longer need to keep nightly WebKit builds working with Safari 4.0.x and earlier. +/*! + @method _largestChildWithScrollBars + @abstract Of the child WebFrameViews that are displaying scroll bars, determines which has the largest area. + @result A child WebFrameView that is displaying scroll bars, or nil if none. + */ +- (WebFrameView *)_largestChildWithScrollBars; + +// FIXME: This method was used by Safari 4.0.x and older versions, but has not been used by any other WebKit +// clients to my knowledge, and will not be used by future versions of Safari. It can probably be removed +// once we no longer need to keep nightly WebKit builds working with Safari 4.0.x and earlier. +/*! + @method _hasScrollBars + @result YES if at least one scroll bar is currently displayed + */ +- (BOOL)_hasScrollBars; + +/*! + @method _largestScrollableChild + @abstract Of the child WebFrameViews that allow scrolling, determines which has the largest area. + @result A child WebFrameView that is scrollable, or nil if none. + */ +- (WebFrameView *)_largestScrollableChild; + +/*! + @method _isScrollable + @result YES if scrolling is currently possible, whether or not scroll bars are currently showing. This + differs from -allowsScrolling in that the latter method only checks whether scrolling has been + explicitly disallowed via a call to setAllowsScrolling:NO. + */ +- (BOOL)_isScrollable; + +/*! + @method _contentView + @result The content view (NSClipView) of the WebFrameView's scroll view. + */ +- (NSClipView *)_contentView; + +/*! + @method _customScrollViewClass + @result The custom scroll view class that is installed, nil if the default scroll view is being used. + */ +- (Class)_customScrollViewClass; + +/*! + @method _setCustomScrollViewClass: + @result Switches the WebFrameView's scroll view class, this class needs to be a subclass of WebDynamicScrollBarsView. + Passing nil will switch back to the default WebDynamicScrollBarsView class. + */ +- (void)_setCustomScrollViewClass:(Class)scrollViewClass; + +@end diff --git a/Source/WebKit/mac/WebView/WebFullScreenController.h b/Source/WebKit/mac/WebView/WebFullScreenController.h new file mode 100644 index 0000000..f867dc2 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebFullScreenController.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#if ENABLE(FULLSCREEN_API) + +#import <wtf/RefPtr.h> + +@class WebWindowFadeAnimation; +@class WebView; +namespace WebCore { + class Element; + class RenderBox; + class EventListener; +} + +@interface WebFullScreenController : NSWindowController { +@private + RefPtr<WebCore::Element> _element; + WebCore::RenderBox* _renderer; // (set) + RefPtr<WebCore::Element> _replacementElement; + NSWindow *_backgroundFullscreenWindow; // (retain) + WebWindowFadeAnimation *_fadeAnimation; // (retain) + WebView *_webView; + NSView* _placeholderView; + CALayer* _rendererLayer; + CALayer* _backgroundLayer; + RefPtr<WebCore::EventListener> _mediaEventListener; + + BOOL _isAnimating; + BOOL _isFullscreen; + BOOL _isWindowLoaded; + BOOL _forceDisableAnimation; + BOOL _isPlaying; + uint32_t _idleDisplaySleepAssertion; + uint32_t _idleSystemSleepAssertion; + NSTimer *_tickleTimer; + SystemUIMode _savedUIMode; + SystemUIOptions _savedUIOptions; + CGRect _initialFrame; +} + +- (WebView*)webView; +- (void)setWebView:(WebView*)webView; + +- (void)setElement:(PassRefPtr<WebCore::Element>)element; +- (WebCore::Element*)element; + +- (void)setRenderer:(WebCore::RenderBox*)renderer; +- (WebCore::RenderBox*)renderer; + +- (void)enterFullscreen:(NSScreen *)screen; +- (void)exitFullscreen; + +@end + +#endif // ENABLE(FULLSCREEN_API) diff --git a/Source/WebKit/mac/WebView/WebFullScreenController.mm b/Source/WebKit/mac/WebView/WebFullScreenController.mm new file mode 100644 index 0000000..6529435 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebFullScreenController.mm @@ -0,0 +1,924 @@ +/* + * Copyright (C) 2009, 2010, 2011 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#if ENABLE(FULLSCREEN_API) + +#import "WebFullScreenController.h" + +#import "WebPreferencesPrivate.h" +#import "WebWindowAnimation.h" +#import "WebViewInternal.h" +#import <IOKit/pwr_mgt/IOPMLib.h> +#import <OSServices/Power.h> +#import <WebCore/AnimationList.h> +#import <WebCore/CSSPropertyNames.h> +#import <WebCore/Color.h> +#import <WebCore/Document.h> +#import <WebCore/DOMDocument.h> +#import <WebCore/DOMDocumentInternal.h> +#import <WebCore/DOMHTMLElement.h> +#import <WebCore/DOMWindow.h> +#import <WebCore/EventListener.h> +#import <WebCore/EventNames.h> +#import <WebCore/HTMLElement.h> +#import <WebCore/HTMLNames.h> +#import <WebCore/HTMLMediaElement.h> +#import <WebCore/IntRect.h> +#import <WebCore/NodeList.h> +#import <WebCore/SoftLinking.h> +#import <WebCore/RenderBlock.h> +#import <WebCore/RenderLayer.h> +#import <WebCore/RenderLayerBacking.h> +#import <objc/objc-runtime.h> +#import <wtf/UnusedParam.h> + +static const NSTimeInterval tickleTimerInterval = 1.0; +static NSString* const isEnteringFullscreenKey = @"isEnteringFullscreen"; + +using namespace WebCore; + +#if defined(BUILDING_ON_LEOPARD) +@interface CATransaction(SnowLeopardConvenienceFunctions) ++ (void)setDisableActions:(BOOL)flag; ++ (void)setAnimationDuration:(CFTimeInterval)dur; +@end + +@implementation CATransaction(SnowLeopardConvenienceFunctions) ++ (void)setDisableActions:(BOOL)flag +{ + [self setValue:[NSNumber numberWithBool:flag] forKey:kCATransactionDisableActions]; +} + ++ (void)setAnimationDuration:(CFTimeInterval)dur +{ + [self setValue:[NSNumber numberWithDouble:dur] forKey:kCATransactionAnimationDuration]; +} +@end + +#endif + +@interface WebFullscreenWindow : NSWindow +#if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_TIGER) +<NSAnimationDelegate> +#endif +{ + NSView* _animationView; + + CALayer* _rendererLayer; + CALayer* _backgroundLayer; +} +- (CALayer*)rendererLayer; +- (void)setRendererLayer:(CALayer*)rendererLayer; +- (CALayer*)backgroundLayer; +- (NSView*)animationView; +@end + +class MediaEventListener : public EventListener { +public: + static PassRefPtr<MediaEventListener> create(WebFullScreenController* delegate); + virtual bool operator==(const EventListener&); + virtual void handleEvent(ScriptExecutionContext*, Event*); + +private: + MediaEventListener(WebFullScreenController* delegate); + WebFullScreenController* delegate; +}; + +@interface WebFullScreenController(Private) +- (void)_requestExitFullscreenWithAnimation:(BOOL)animation; +- (void)_updateMenuAndDockForFullscreen; +- (void)_updatePowerAssertions; +- (WebFullscreenWindow *)_fullscreenWindow; +- (Document*)_document; +- (CFTimeInterval)_animationDuration; +- (BOOL)_isAnyMoviePlaying; +@end + +@interface NSWindow(IsOnActiveSpaceAdditionForTigerAndLeopard) +- (BOOL)isOnActiveSpace; +@end + +@implementation WebFullScreenController + +#pragma mark - +#pragma mark Initialization +- (id)init +{ + // Do not defer window creation, to make sure -windowNumber is created (needed by WebWindowScaleAnimation). + NSWindow *window = [[WebFullscreenWindow alloc] initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; + self = [super initWithWindow:window]; + [window release]; + if (!self) + return nil; + [self windowDidLoad]; + _mediaEventListener = MediaEventListener::create(self); + return self; + +} + +- (void)dealloc +{ + ASSERT(!_tickleTimer); + + [self setWebView:nil]; + [_placeholderView release]; + + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; +} + +- (void)windowDidLoad +{ +#ifdef BUILDING_ON_TIGER + // WebFullScreenController is not supported on Tiger: + ASSERT_NOT_REACHED(); +#else + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidResignActive:) name:NSApplicationDidResignActiveNotification object:NSApp]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidChangeScreenParameters:) name:NSApplicationDidChangeScreenParametersNotification object:NSApp]; +#endif +} + +#pragma mark - +#pragma mark Accessors + +- (WebView*)webView +{ + return _webView; +} + +- (void)setWebView:(WebView *)webView +{ + [webView retain]; + [_webView release]; + _webView = webView; +} + +- (Element*)element +{ + return _element.get(); +} + +- (void)setElement:(PassRefPtr<Element>)element +{ +#ifdef BUILDING_ON_TIGER + // WebFullScreenController is not supported on Tiger: + ASSERT_NOT_REACHED(); +#else + // When a new Element is set as the current full screen element, register event + // listeners on that Element's window, listening for changes in media play states. + // We will use these events to determine whether to disable the screensaver and + // display sleep timers when playing video in full screen. Make sure to unregister + // the events on the old element's window, if necessary, as well. + + EventNames& eventNames = WebCore::eventNames(); + + if (_element) { + DOMWindow* window = _element->document()->domWindow(); + if (window) { + window->removeEventListener(eventNames.playEvent, _mediaEventListener.get(), true); + window->removeEventListener(eventNames.pauseEvent, _mediaEventListener.get(), true); + window->removeEventListener(eventNames.endedEvent, _mediaEventListener.get(), true); + } + } + + _element = element; + + if (_element) { + DOMWindow* window = _element->document()->domWindow(); + if (window) { + window->addEventListener(eventNames.playEvent, _mediaEventListener, true); + window->addEventListener(eventNames.pauseEvent, _mediaEventListener, true); + window->addEventListener(eventNames.endedEvent, _mediaEventListener, true); + } + } +#endif +} + +- (RenderBox*)renderer +{ + return _renderer; +} + +- (void)setRenderer:(RenderBox*)renderer +{ +#ifdef BUILDING_ON_TIGER + // WebFullScreenController is not supported on Tiger: + ASSERT_NOT_REACHED(); +#else + _renderer = renderer; +#endif +} + +#pragma mark - +#pragma mark Notifications + +- (void)windowDidExitFullscreen:(BOOL)finished +{ + if (!_isAnimating) + return; + + if (_isFullscreen) + return; + + NSDisableScreenUpdates(); + ASSERT(_element); + [self _document]->setFullScreenRendererBackgroundColor(Color::black); + [self _document]->webkitDidExitFullScreenForElement(_element.get()); + [self setElement:nil]; + + if (finished) { + [self _updateMenuAndDockForFullscreen]; + [self _updatePowerAssertions]; + + [[_webView window] display]; + [[self _fullscreenWindow] setRendererLayer:nil]; + [[self window] close]; + } + + NSEnableScreenUpdates(); + + _isAnimating = NO; + [self autorelease]; // Associated -retain is in -exitFullscreen. +} + +- (void)windowDidEnterFullscreen:(BOOL)finished +{ + if (!_isAnimating) + return; + + if (!_isFullscreen) + return; + + NSDisableScreenUpdates(); + [self _document]->webkitDidEnterFullScreenForElement(_element.get()); + [self _document]->setFullScreenRendererBackgroundColor(Color::black); + + if (finished) { + [self _updateMenuAndDockForFullscreen]; + [self _updatePowerAssertions]; + [NSCursor setHiddenUntilMouseMoves:YES]; + + // Move the webView into our fullscreen Window + if (!_placeholderView) + _placeholderView = [[NSView alloc] init]; + + // Do not swap the placeholder into place if already is in a window, + // assuming the placeholder's window will always be the webView's + // original window. + if (![_placeholderView window]) { + WebView* webView = [self webView]; + [_placeholderView setFrame:[webView frame]]; + [_placeholderView setAutoresizingMask:[webView autoresizingMask]]; + [_placeholderView removeFromSuperview]; + [[webView superview] replaceSubview:webView with:_placeholderView]; + + [[[self window] contentView] addSubview:webView]; + [webView setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; + [webView setFrame:[[[self window] contentView] bounds]]; + } + + WebFullscreenWindow* window = [self _fullscreenWindow]; + [window setBackgroundColor:[NSColor blackColor]]; + [window setOpaque:YES]; + + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + [[[window animationView] layer] setOpacity:0]; + [CATransaction commit]; + } + NSEnableScreenUpdates(); + + _isAnimating = NO; +} + +- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)finished +{ + BOOL isEnteringFullscreenAnimation = [[anim valueForKey:isEnteringFullscreenKey] boolValue]; + + if (!isEnteringFullscreenAnimation) + [self windowDidExitFullscreen:finished]; + else + [self windowDidEnterFullscreen:finished]; +} + +- (void)applicationDidResignActive:(NSNotification*)notification +{ + // Check to see if the fullscreenWindow is on the active space; this function is available + // on 10.6 and later, so default to YES if the function is not available: + NSWindow* fullscreenWindow = [self _fullscreenWindow]; + BOOL isOnActiveSpace = ([fullscreenWindow respondsToSelector:@selector(isOnActiveSpace)] ? [fullscreenWindow isOnActiveSpace] : YES); + + // Replicate the QuickTime Player (X) behavior when losing active application status: + // Is the fullscreen screen the main screen? (Note: this covers the case where only a + // single screen is available.) Is the fullscreen screen on the current space? IFF so, + // then exit fullscreen mode. + if ([fullscreenWindow screen] == [[NSScreen screens] objectAtIndex:0] && isOnActiveSpace) + [self _requestExitFullscreenWithAnimation:NO]; +} + +- (void)applicationDidChangeScreenParameters:(NSNotification*)notification +{ + // The user may have changed the main screen by moving the menu bar, or they may have changed + // the Dock's size or location, or they may have changed the fullscreen screen's dimensions. + // Update our presentation parameters, and ensure that the full screen window occupies the + // entire screen: + [self _updateMenuAndDockForFullscreen]; + NSWindow* window = [self window]; + [window setFrame:[[window screen] frame] display:YES]; +} + +#pragma mark - +#pragma mark Exposed Interface + +- (void)enterFullscreen:(NSScreen *)screen +{ + // Disable animation if we are already in full-screen mode. + BOOL shouldAnimate = !_isFullscreen; + + if (_isAnimating) { + // The CAAnimation delegate functions will only be called the + // next trip through the run-loop, so manually call the delegate + // function here, letting it know the animation did not complete: + [self windowDidExitFullscreen:NO]; + ASSERT(!_isAnimating); + } + _isFullscreen = YES; + _isAnimating = YES; + + // setElement: must be called with a non-nil value before calling enterFullscreen:. + ASSERT(_element); + + NSDisableScreenUpdates(); + + if (!screen) + screen = [NSScreen mainScreen]; + NSRect screenFrame = [screen frame]; + + WebView* webView = [self webView]; + NSRect webViewFrame = [webView convertRectToBase:[webView frame]]; + webViewFrame.origin = [[webView window] convertBaseToScreen:webViewFrame.origin]; + + NSRect elementFrame = _element->screenRect(); + + // In the case of a multi-monitor setup where the webView straddles two + // monitors, we must create a window large enough to contain the destination + // frame and the initial frame. + NSRect windowFrame = NSUnionRect(screenFrame, elementFrame); + [[self window] setFrame:windowFrame display:YES]; + + // In a previous incarnation, the NSWindow attached to this controller may have + // been on a different screen. Temporarily change the collectionBehavior of the window: + NSWindowCollectionBehavior behavior = [[self window] collectionBehavior]; + [[self window] setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces]; + [[self window] makeKeyAndOrderFront:self]; + [[self window] setCollectionBehavior:behavior]; + + NSView* animationView = [[self _fullscreenWindow] animationView]; + NSRect viewBounds = [animationView bounds]; + + NSRect backgroundBounds = {[[self window] convertScreenToBase:screenFrame.origin], screenFrame.size}; + backgroundBounds = [animationView convertRectFromBase:backgroundBounds]; + // Flip the background layer's coordinate system. + backgroundBounds.origin.y = windowFrame.size.height - NSMaxY(backgroundBounds); + + // Set our fullscreen element's initial frame, and flip the coordinate systems from + // screen coordinates (bottom/left) to layer coordinates (top/left): + _initialFrame = NSRectToCGRect(NSIntersectionRect(elementFrame, webViewFrame)); + _initialFrame.origin.y = screenFrame.size.height - CGRectGetMaxY(_initialFrame); + + // Inform the document that we will begin entering full screen. This will change + // pseudo-classes on the fullscreen element and the document element. + Document* document = [self _document]; + document->webkitWillEnterFullScreenForElement(_element.get()); + + // Check to see if the fullscreen renderer is composited. If not, accelerated graphics + // may be disabled. In this case, do not attempt to animate the contents into place; + // merely snap to the final position: + if (!shouldAnimate || !_renderer || !_renderer->layer()->isComposited()) { + [self windowDidEnterFullscreen:YES]; + NSEnableScreenUpdates(); + return; + } + + // Set up the final style of the FullScreen render block. Set an absolute + // width and height equal to the size of the screen, and anchor the layer + // at the top, left at (0,0). The RenderFullScreen style is already set + // to position:fixed. + [self _document]->setFullScreenRendererSize(IntSize(screenFrame.size)); + [self _document]->setFullScreenRendererBackgroundColor(Color::transparent); + + // Cause the document to layout, thus calculating a new fullscreen element size: + [self _document]->updateLayout(); + + // FIXME: try to use the fullscreen element's calculated x, y, width, and height instead of the + // renderBox functions: + RenderBox* childRenderer = _renderer->firstChildBox(); + CGRect destinationFrame = CGRectMake(childRenderer->x(), childRenderer->y(), childRenderer->width(), childRenderer->height()); + + // Some properties haven't propogated from the GraphicsLayer to the CALayer yet. So + // tell the renderer's layer to sync it's compositing state: + GraphicsLayer* rendererGraphics = _renderer->layer()->backing()->graphicsLayer(); + rendererGraphics->syncCompositingState(); + + CALayer* rendererLayer = rendererGraphics->platformLayer(); + [[self _fullscreenWindow] setRendererLayer:rendererLayer]; + + CFTimeInterval duration = [self _animationDuration]; + + // Create a transformation matrix that will transform the renderer layer such that + // the fullscreen element appears to move from its starting position and size to its + // final one. Perform the transformation in two steps, using the CALayer's matrix + // math to calculate the effects of each step: + // 1. Apply a scale tranform to shrink the apparent size of the layer to the original + // element screen size. + // 2. Apply a translation transform to move the shrunk layer into the same screen position + // as the original element. + CATransform3D shrinkTransform = CATransform3DMakeScale(_initialFrame.size.width / destinationFrame.size.width, _initialFrame.size.height / destinationFrame.size.height, 1); + [rendererLayer setTransform:shrinkTransform]; + CGRect shrunkDestinationFrame = [rendererLayer convertRect:destinationFrame toLayer:[animationView layer]]; + CATransform3D moveTransform = CATransform3DMakeTranslation(_initialFrame.origin.x - shrunkDestinationFrame.origin.x, _initialFrame.origin.y - shrunkDestinationFrame.origin.y, 0); + CATransform3D finalTransform = CATransform3DConcat(shrinkTransform, moveTransform); + [rendererLayer setTransform:finalTransform]; + CGRect translatedDestinationFrame = [rendererLayer convertRect:destinationFrame toLayer:[animationView layer]]; + + CALayer* backgroundLayer = [[self _fullscreenWindow] backgroundLayer]; + + // Start the opacity animation. We can use implicit animations here because we don't care when + // the animation finishes. + [CATransaction begin]; + [CATransaction setAnimationDuration:duration]; + [backgroundLayer setOpacity:1]; + [CATransaction commit]; + + // Use a CABasicAnimation here for the zoom effect. We want to be notified that the animation has + // completed by way of the CAAnimation delegate. + CABasicAnimation* zoomAnimation = [CABasicAnimation animationWithKeyPath:@"transform"]; + [zoomAnimation setFromValue:[NSValue valueWithCATransform3D:finalTransform]]; + [zoomAnimation setToValue:[NSValue valueWithCATransform3D:CATransform3DIdentity]]; + [zoomAnimation setDelegate:self]; + [zoomAnimation setDuration:duration]; + [zoomAnimation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]]; + [zoomAnimation setFillMode:kCAFillModeForwards]; + [zoomAnimation setValue:(id)kCFBooleanTrue forKey:isEnteringFullscreenKey]; + + // Disable implicit animations and set the layer's transformation matrix to its final state. + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + [rendererLayer setTransform:CATransform3DIdentity]; + [rendererLayer addAnimation:zoomAnimation forKey:@"zoom"]; + [backgroundLayer setFrame:NSRectToCGRect(backgroundBounds)]; + [CATransaction commit]; + + NSEnableScreenUpdates(); +} + +- (void)exitFullscreen +{ + if (!_isFullscreen) + return; + + CATransform3D startTransform = CATransform3DIdentity; + if (_isAnimating) { + if (_renderer && _renderer->layer()->isComposited()) { + CALayer* rendererLayer = _renderer->layer()->backing()->graphicsLayer()->platformLayer(); + startTransform = [[rendererLayer presentationLayer] transform]; + } + + // The CAAnimation delegate functions will only be called the + // next trip through the run-loop, so manually call the delegate + // function here, letting it know the animation did not complete: + [self windowDidEnterFullscreen:NO]; + ASSERT(!_isAnimating); + } + _isFullscreen = NO; + _isAnimating = YES; + + NSDisableScreenUpdates(); + + // The user may have moved the fullscreen window in Spaces, so temporarily change + // the collectionBehavior of the webView's window: + NSWindow* webWindow = [[self webView] window]; + NSWindowCollectionBehavior behavior = [webWindow collectionBehavior]; + [webWindow setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces]; + [webWindow orderWindow:NSWindowBelow relativeTo:[[self window] windowNumber]]; + [webWindow setCollectionBehavior:behavior]; + + // The fullscreen animation may have been cancelled before the + // webView was moved to the fullscreen window. Check to see + // if the _placeholderView exists and is in a window before + // attempting to swap the webView back to it's original tree: + if (_placeholderView && [_placeholderView window]) { + // Move the webView back to its own native window: + WebView* webView = [self webView]; + [webView setFrame:[_placeholderView frame]]; + [webView setAutoresizingMask:[_placeholderView autoresizingMask]]; + [webView removeFromSuperview]; + [[_placeholderView superview] replaceSubview:_placeholderView with:webView]; + + // Because the animation view is layer-hosted, make sure to + // disable animations when changing the layer's opacity. Other- + // wise, the content will appear to fade into view. + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + WebFullscreenWindow* window = [self _fullscreenWindow]; + [[[window animationView] layer] setOpacity:1]; + [window setBackgroundColor:[NSColor clearColor]]; + [window setOpaque:NO]; + [CATransaction commit]; + } + + NSView* animationView = [[self _fullscreenWindow] animationView]; + CGRect layerEndFrame = NSRectToCGRect([animationView convertRect:NSRectFromCGRect(_initialFrame) fromView:nil]); + + // The _renderer might be NULL due to its ancestor being removed: + CGRect layerStartFrame = CGRectZero; + if (_renderer) { + RenderBox* childRenderer = _renderer->firstChildBox(); + layerStartFrame = CGRectMake(childRenderer->x(), childRenderer->y(), childRenderer->width(), childRenderer->height()); + } + + [self _document]->webkitWillExitFullScreenForElement(_element.get()); + [self _document]->updateLayout(); + + // We have to retain ourselves because we want to be alive for the end of the animation. + // If our owner releases us we could crash if this is not the case. + // Balanced in windowDidExitFullscreen + [self retain]; + + // Check to see if the fullscreen renderer is composited. If not, accelerated graphics + // may be disabled. In this case, do not attempt to animate the contents into place; + // merely snap to the final position: + if (!_renderer || !_renderer->layer()->isComposited()) { + [self windowDidExitFullscreen:YES]; + NSEnableScreenUpdates(); + return; + } + + GraphicsLayer* rendererGraphics = _renderer->layer()->backing()->graphicsLayer(); + + [self _document]->setFullScreenRendererBackgroundColor(Color::transparent); + + rendererGraphics->syncCompositingState(); + + CALayer* rendererLayer = rendererGraphics->platformLayer(); + [[self _fullscreenWindow] setRendererLayer:rendererLayer]; + + // Create a transformation matrix that will transform the renderer layer such that + // the fullscreen element appears to move from the full screen to its original position + // and size. Perform the transformation in two steps, using the CALayer's matrix + // math to calculate the effects of each step: + // 1. Apply a scale tranform to shrink the apparent size of the layer to the original + // element screen size. + // 2. Apply a translation transform to move the shrunk layer into the same screen position + // as the original element. + CATransform3D shrinkTransform = CATransform3DMakeScale(layerEndFrame.size.width / layerStartFrame.size.width, layerEndFrame.size.height / layerStartFrame.size.height, 1); + [rendererLayer setTransform:shrinkTransform]; + CGRect shrunkDestinationFrame = [rendererLayer convertRect:layerStartFrame toLayer:[animationView layer]]; + CATransform3D moveTransform = CATransform3DMakeTranslation(layerEndFrame.origin.x - shrunkDestinationFrame.origin.x, layerEndFrame.origin.y - shrunkDestinationFrame.origin.y, 0); + CATransform3D finalTransform = CATransform3DConcat(shrinkTransform, moveTransform); + [rendererLayer setTransform:finalTransform]; + CGRect translatedDestinationFrame = [rendererLayer convertRect:layerStartFrame toLayer:[animationView layer]]; + + CFTimeInterval duration = [self _animationDuration]; + + CALayer* backgroundLayer = [[self _fullscreenWindow] backgroundLayer]; + [CATransaction begin]; + [CATransaction setAnimationDuration:duration]; + [backgroundLayer setOpacity:0]; + [CATransaction commit]; + + CABasicAnimation* zoomAnimation = [CABasicAnimation animationWithKeyPath:@"transform"]; + [zoomAnimation setFromValue:[NSValue valueWithCATransform3D:startTransform]]; + [zoomAnimation setToValue:[NSValue valueWithCATransform3D:finalTransform]]; + [zoomAnimation setDelegate:self]; + [zoomAnimation setDuration:duration]; + [zoomAnimation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]]; + [zoomAnimation setFillMode:kCAFillModeBoth]; + [zoomAnimation setRemovedOnCompletion:NO]; + [zoomAnimation setValue:(id)kCFBooleanFalse forKey:isEnteringFullscreenKey]; + + [rendererLayer addAnimation:zoomAnimation forKey:@"zoom"]; + + NSEnableScreenUpdates(); +} + +#pragma mark - +#pragma mark Internal Interface + +- (void)_updateMenuAndDockForFullscreen +{ + // NSApplicationPresentationOptions is available on > 10.6 only: +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + NSApplicationPresentationOptions options = NSApplicationPresentationDefault; + NSScreen* fullscreenScreen = [[self window] screen]; + + if (_isFullscreen) { + // Auto-hide the menu bar if the fullscreenScreen contains the menu bar: + // NOTE: if the fullscreenScreen contains the menu bar but not the dock, we must still + // auto-hide the dock, or an exception will be thrown. + if ([[NSScreen screens] objectAtIndex:0] == fullscreenScreen) + options |= (NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationAutoHideDock); + // Check if the current screen contains the dock by comparing the screen's frame to its + // visibleFrame; if a dock is present, the visibleFrame will differ. If the current screen + // contains the dock, hide it. + else if (!NSEqualRects([fullscreenScreen frame], [fullscreenScreen visibleFrame])) + options |= NSApplicationPresentationAutoHideDock; + } + + if ([NSApp respondsToSelector:@selector(setPresentationOptions:)]) + [NSApp setPresentationOptions:options]; + else +#endif + SetSystemUIMode(_isFullscreen ? kUIModeNormal : kUIModeAllHidden, 0); +} + +#if !defined(BUILDING_ON_TIGER) // IOPMAssertionCreateWithName not defined on < 10.5 +- (void)_disableIdleDisplaySleep +{ + if (_idleDisplaySleepAssertion == kIOPMNullAssertionID) +#if defined(BUILDING_ON_LEOPARD) // IOPMAssertionCreateWithName is not defined in the 10.5 SDK + IOPMAssertionCreate(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, &_idleDisplaySleepAssertion); +#else // IOPMAssertionCreate is depreciated in > 10.5 + IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, CFSTR("WebKit playing a video fullscreen."), &_idleDisplaySleepAssertion); +#endif +} + +- (void)_enableIdleDisplaySleep +{ + if (_idleDisplaySleepAssertion != kIOPMNullAssertionID) { + IOPMAssertionRelease(_idleDisplaySleepAssertion); + _idleDisplaySleepAssertion = kIOPMNullAssertionID; + } +} + +- (void)_disableIdleSystemSleep +{ + if (_idleSystemSleepAssertion == kIOPMNullAssertionID) +#if defined(BUILDING_ON_LEOPARD) // IOPMAssertionCreateWithName is not defined in the 10.5 SDK + IOPMAssertionCreate(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, &_idleSystemSleepAssertion); +#else // IOPMAssertionCreate is depreciated in > 10.5 + IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, CFSTR("WebKit playing a video fullscreen."), &_idleSystemSleepAssertion); +#endif +} + +- (void)_enableIdleSystemSleep +{ + if (_idleSystemSleepAssertion != kIOPMNullAssertionID) { + IOPMAssertionRelease(_idleSystemSleepAssertion); + _idleSystemSleepAssertion = kIOPMNullAssertionID; + } +} + +- (void)_enableTickleTimer +{ + [_tickleTimer invalidate]; + [_tickleTimer release]; + _tickleTimer = [[NSTimer scheduledTimerWithTimeInterval:tickleTimerInterval target:self selector:@selector(_tickleTimerFired) userInfo:nil repeats:YES] retain]; +} + +- (void)_disableTickleTimer +{ + [_tickleTimer invalidate]; + [_tickleTimer release]; + _tickleTimer = nil; +} + +- (void)_tickleTimerFired +{ + UpdateSystemActivity(OverallAct); +} +#endif + +- (void)_updatePowerAssertions +{ +#if !defined(BUILDING_ON_TIGER) + BOOL isPlaying = [self _isAnyMoviePlaying]; + + if (isPlaying && _isFullscreen) { + [self _disableIdleSystemSleep]; + [self _disableIdleDisplaySleep]; + [self _enableTickleTimer]; + } else { + [self _enableIdleSystemSleep]; + [self _enableIdleDisplaySleep]; + [self _disableTickleTimer]; + } +#endif +} + +- (void)_requestExit +{ + [self exitFullscreen]; + _forceDisableAnimation = NO; +} + +- (void)_requestExitFullscreenWithAnimation:(BOOL)animation +{ + _forceDisableAnimation = !animation; + [self performSelector:@selector(_requestExit) withObject:nil afterDelay:0]; + +} + +- (BOOL)_isAnyMoviePlaying +{ + if (!_element) + return NO; + + Node* nextNode = _element.get(); + while (nextNode) + { + if (nextNode->hasTagName(HTMLNames::videoTag)) { + HTMLMediaElement* element = static_cast<HTMLMediaElement*>(nextNode); + if (!element->paused() && !element->ended()) + return YES; + } + + nextNode = nextNode->traverseNextNode(_element.get()); + } + + return NO; +} + +#pragma mark - +#pragma mark Utility Functions + +- (WebFullscreenWindow *)_fullscreenWindow +{ + return (WebFullscreenWindow *)[self window]; +} + +- (Document*)_document +{ + return core([[[self webView] mainFrame] DOMDocument]); +} + +- (CFTimeInterval)_animationDuration +{ + static const CFTimeInterval defaultDuration = 0.5; + CFTimeInterval duration = defaultDuration; +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + NSUInteger modifierFlags = [NSEvent modifierFlags]; +#else + NSUInteger modifierFlags = [[NSApp currentEvent] modifierFlags]; +#endif + if ((modifierFlags & NSControlKeyMask) == NSControlKeyMask) + duration *= 2; + if ((modifierFlags & NSShiftKeyMask) == NSShiftKeyMask) + duration *= 10; + if (_forceDisableAnimation) { + // This will disable scale animation + duration = 0; + } + return duration; +} + +@end + +#pragma mark - +@implementation WebFullscreenWindow + +- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag +{ + UNUSED_PARAM(aStyle); + self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:bufferingType defer:flag]; + if (!self) + return nil; + [self setOpaque:NO]; + [self setBackgroundColor:[NSColor clearColor]]; + [self setIgnoresMouseEvents:NO]; + [self setAcceptsMouseMovedEvents:YES]; + [self setReleasedWhenClosed:NO]; + [self setHasShadow:YES]; +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + [self setMovable:NO]; +#else + [self setMovableByWindowBackground:NO]; +#endif + + NSView* contentView = [self contentView]; + _animationView = [[NSView alloc] initWithFrame:[contentView bounds]]; + + CALayer* contentLayer = [[CALayer alloc] init]; + [_animationView setLayer:contentLayer]; + [_animationView setWantsLayer:YES]; + [_animationView setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; + [contentView addSubview:_animationView]; + + _backgroundLayer = [[CALayer alloc] init]; + [contentLayer addSublayer:_backgroundLayer]; +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + [contentLayer setGeometryFlipped:YES]; +#else + [contentLayer setSublayerTransform:CATransform3DMakeScale(1, -1, 1)]; +#endif + [contentLayer setOpacity:0]; + + [_backgroundLayer setBackgroundColor:CGColorGetConstantColor(kCGColorBlack)]; + [_backgroundLayer setOpacity:0]; + return self; +} + +- (void)dealloc +{ + [_animationView release]; + [_backgroundLayer release]; + [_rendererLayer release]; + [super dealloc]; +} + +- (BOOL)canBecomeKeyWindow +{ + return YES; +} + +- (void)keyDown:(NSEvent *)theEvent +{ + if ([[theEvent charactersIgnoringModifiers] isEqual:@"\e"]) // Esacpe key-code + [self cancelOperation:self]; + else [super keyDown:theEvent]; +} + +- (void)cancelOperation:(id)sender +{ + UNUSED_PARAM(sender); + [[self windowController] _requestExitFullscreenWithAnimation:YES]; +} + +- (CALayer*)rendererLayer +{ + return _rendererLayer; +} + +- (void)setRendererLayer:(CALayer *)rendererLayer +{ + [CATransaction begin]; + [CATransaction setDisableActions:YES]; + [rendererLayer retain]; + [_rendererLayer removeFromSuperlayer]; + [_rendererLayer release]; + _rendererLayer = rendererLayer; + + if (_rendererLayer) + [[[self animationView] layer] addSublayer:_rendererLayer]; + [CATransaction commit]; +} + +- (CALayer*)backgroundLayer +{ + return _backgroundLayer; +} + +- (NSView*)animationView +{ + return _animationView; +} +@end + +#pragma mark - +#pragma mark MediaEventListener + +MediaEventListener::MediaEventListener(WebFullScreenController* delegate) + : EventListener(CPPEventListenerType) + , delegate(delegate) +{ +} + +PassRefPtr<MediaEventListener> MediaEventListener::create(WebFullScreenController* delegate) +{ + return adoptRef(new MediaEventListener(delegate)); +} + +bool MediaEventListener::operator==(const EventListener& listener) +{ + return this == &listener; +} + +void MediaEventListener::handleEvent(ScriptExecutionContext* context, Event* event) +{ + [delegate _updatePowerAssertions]; +} + +#endif /* ENABLE(FULLSCREEN_API) */ diff --git a/Source/WebKit/mac/WebView/WebGeolocationPosition.h b/Source/WebKit/mac/WebView/WebGeolocationPosition.h new file mode 100644 index 0000000..99348b4 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebGeolocationPosition.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +@class WebGeolocationPositionInternal; + +@interface WebGeolocationPosition : NSObject +{ + WebGeolocationPositionInternal *_internal; +} + +- (id)initWithTimestamp:(double)timestamp latitude:(double)latitude longitude:(double)longitude accuracy:(double)accuracy; + +@end diff --git a/Source/WebKit/mac/WebView/WebGeolocationPosition.mm b/Source/WebKit/mac/WebView/WebGeolocationPosition.mm new file mode 100644 index 0000000..c92b7f1 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebGeolocationPosition.mm @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "WebGeolocationPosition.h" + +#import "WebGeolocationPositionInternal.h" +#import <wtf/PassRefPtr.h> +#import <wtf/RefPtr.h> + +#if ENABLE(CLIENT_BASED_GEOLOCATION) +#import <WebCore/GeolocationPosition.h> + +using namespace WebCore; +#endif + +@interface WebGeolocationPositionInternal : NSObject +#if ENABLE(CLIENT_BASED_GEOLOCATION) +{ +@public + RefPtr<GeolocationPosition> _position; +} + +- (id)initWithCoreGeolocationPosition:(PassRefPtr<GeolocationPosition>)coreGeolocationPosition; +#endif +@end + +@implementation WebGeolocationPositionInternal + +#if ENABLE(CLIENT_BASED_GEOLOCATION) +- (id)initWithCoreGeolocationPosition:(PassRefPtr<GeolocationPosition>)coreGeolocationPosition +{ + self = [super init]; + if (!self) + return nil; + _position = coreGeolocationPosition; + return self; +} +#endif + +@end + +@implementation WebGeolocationPosition + +#if ENABLE(CLIENT_BASED_GEOLOCATION) +GeolocationPosition* core(WebGeolocationPosition *position) +{ + return position ? position->_internal->_position.get() : 0; +} +#endif + +- (id)initWithTimestamp:(double)timestamp latitude:(double)latitude longitude:(double)longitude accuracy:(double)accuracy +{ + self = [super init]; + if (!self) + return nil; +#if ENABLE(CLIENT_BASED_GEOLOCATION) + _internal = [[WebGeolocationPositionInternal alloc] initWithCoreGeolocationPosition:GeolocationPosition::create(timestamp, latitude, longitude, accuracy)]; +#else + _internal = [[WebGeolocationPositionInternal alloc] init]; +#endif + return self; +} + +- (void)dealloc +{ + [_internal release]; + [super dealloc]; +} + +@end diff --git a/Source/WebKit/mac/WebView/WebGeolocationPositionInternal.h b/Source/WebKit/mac/WebView/WebGeolocationPositionInternal.h new file mode 100644 index 0000000..27f8a39 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebGeolocationPositionInternal.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "WebGeolocationPosition.h" + +#if ENABLE(CLIENT_BASED_GEOLOCATION) + +namespace WebCore { + class GeolocationPosition; +} + +WebCore::GeolocationPosition* core(WebGeolocationPosition *); + +#endif diff --git a/Source/WebKit/mac/WebView/WebHTMLRepresentation.h b/Source/WebKit/mac/WebView/WebHTMLRepresentation.h new file mode 100644 index 0000000..68f9bde --- /dev/null +++ b/Source/WebKit/mac/WebView/WebHTMLRepresentation.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * 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 <Foundation/Foundation.h> + +#import <WebKit/WebDocumentPrivate.h> + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 +#define WebNSUInteger unsigned int +#else +#define WebNSUInteger NSUInteger +#endif + +@class WebHTMLRepresentationPrivate; +@class NSView; + +@class DOMNode; +@class DOMElement; + +@protocol WebDocumentMarkup; +@protocol WebDocumentRepresentation; +@protocol WebDocumentSourceRepresentation; + +/*! + @class WebHTMLRepresentation +*/ +@interface WebHTMLRepresentation : NSObject <WebDocumentRepresentation, WebDocumentDOM> +{ + WebHTMLRepresentationPrivate *_private; +} + ++ (NSArray *)supportedMIMETypes; ++ (NSArray *)supportedNonImageMIMETypes; ++ (NSArray *)supportedImageMIMETypes; + +- (NSAttributedString *)attributedStringFrom:(DOMNode *)startNode startOffset:(int)startOffset to:(DOMNode *)endNode endOffset:(int)endOffset; + +- (DOMElement *)elementWithName:(NSString *)name inForm:(DOMElement *)form; +- (BOOL)elementDoesAutoComplete:(DOMElement *)element; +- (BOOL)elementIsPassword:(DOMElement *)element; +- (DOMElement *)formForElement:(DOMElement *)element; +- (DOMElement *)currentForm; +- (NSArray *)controlsInForm:(DOMElement *)form; +- (NSString *)searchForLabels:(NSArray *)labels beforeElement:(DOMElement *)element resultDistance:(WebNSUInteger*)outDistance resultIsInCellAbove:(BOOL*)outIsInCellAbove; +- (NSString *)matchLabels:(NSArray *)labels againstElement:(DOMElement *)element; + +// Deprecated SPI +- (NSString *)searchForLabels:(NSArray *)labels beforeElement:(DOMElement *)element; // Use -searchForLabels:beforeElement:resultDistance:resultIsInCellAbove: + +@end + +#undef WebNSUInteger diff --git a/Source/WebKit/mac/WebView/WebHTMLRepresentation.mm b/Source/WebKit/mac/WebView/WebHTMLRepresentation.mm new file mode 100644 index 0000000..fad0a4a --- /dev/null +++ b/Source/WebKit/mac/WebView/WebHTMLRepresentation.mm @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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 "WebHTMLRepresentation.h" + +#import "DOMElementInternal.h" +#import "DOMNodeInternal.h" +#import "DOMRangeInternal.h" +#import "WebArchive.h" +#import "WebBasePluginPackage.h" +#import "WebDataSourceInternal.h" +#import "WebDocumentPrivate.h" +#import "WebFrameInternal.h" +#import "WebKitNSStringExtras.h" +#import "WebKitStatisticsPrivate.h" +#import "WebNSAttributedStringExtras.h" +#import "WebNSObjectExtras.h" +#import "WebTypesInternal.h" +#import "WebView.h" +#import <Foundation/NSURLResponse.h> +#import <WebCore/Document.h> +#import <WebCore/DocumentLoader.h> +#import <WebCore/Frame.h> +#import <WebCore/FrameLoader.h> +#import <WebCore/FrameLoaderClient.h> +#import <WebCore/HTMLFormControlElement.h> +#import <WebCore/HTMLFormElement.h> +#import <WebCore/HTMLInputElement.h> +#import <WebCore/HTMLNames.h> +#import <WebCore/MIMETypeRegistry.h> +#import <WebCore/Range.h> +#import <WebCore/TextResourceDecoder.h> +#import <WebKit/DOMHTMLInputElement.h> +#import <wtf/Assertions.h> +#import <wtf/StdLibExtras.h> + +using namespace WebCore; +using namespace HTMLNames; + +@interface WebHTMLRepresentationPrivate : NSObject { +@public + WebDataSource *dataSource; + + BOOL hasSentResponseToPlugin; + BOOL includedInWebKitStatistics; + + id <WebPluginManualLoader> manualLoader; + NSView *pluginView; +} +@end + +@implementation WebHTMLRepresentationPrivate +@end + +@implementation WebHTMLRepresentation + +static NSArray *stringArray(const HashSet<String>& set) +{ + NSMutableArray *array = [NSMutableArray arrayWithCapacity:set.size()]; + HashSet<String>::const_iterator end = set.end(); + for (HashSet<String>::const_iterator it = set.begin(); it != end; ++it) + [array addObject:(NSString *)(*it)]; + return array; +} + +static NSArray *concatenateArrays(NSArray *first, NSArray *second) +{ + NSMutableArray *result = [[first mutableCopy] autorelease]; + [result addObjectsFromArray:second]; + return result; +} + ++ (NSArray *)supportedMIMETypes +{ + DEFINE_STATIC_LOCAL(RetainPtr<NSArray>, staticSupportedMIMETypes, (concatenateArrays([self supportedNonImageMIMETypes], [self supportedImageMIMETypes]))); + return staticSupportedMIMETypes.get(); +} + ++ (NSArray *)supportedNonImageMIMETypes +{ + DEFINE_STATIC_LOCAL(RetainPtr<NSArray>, staticSupportedNonImageMIMETypes, (stringArray(MIMETypeRegistry::getSupportedNonImageMIMETypes()))); + return staticSupportedNonImageMIMETypes.get(); +} + ++ (NSArray *)supportedImageMIMETypes +{ + DEFINE_STATIC_LOCAL(RetainPtr<NSArray>, staticSupportedImageMIMETypes, (stringArray(MIMETypeRegistry::getSupportedImageMIMETypes()))); + return staticSupportedImageMIMETypes.get(); +} + +- (id)init +{ + self = [super init]; + if (!self) + return nil; + + _private = [[WebHTMLRepresentationPrivate alloc] init]; + + return self; +} + +- (void)dealloc +{ + if (_private && _private->includedInWebKitStatistics) + --WebHTMLRepresentationCount; + + [_private release]; + + [super dealloc]; +} + +- (void)finalize +{ + if (_private && _private->includedInWebKitStatistics) + --WebHTMLRepresentationCount; + + [super finalize]; +} + +- (void)_redirectDataToManualLoader:(id<WebPluginManualLoader>)manualLoader forPluginView:(NSView *)pluginView +{ + _private->manualLoader = manualLoader; + _private->pluginView = pluginView; +} + +- (void)setDataSource:(WebDataSource *)dataSource +{ + _private->dataSource = dataSource; + + if (!_private->includedInWebKitStatistics && [[dataSource webFrame] _isIncludedInWebKitStatistics]) { + _private->includedInWebKitStatistics = YES; + ++WebHTMLRepresentationCount; + } +} + +- (BOOL)_isDisplayingWebArchive +{ + return [[_private->dataSource _responseMIMEType] _webkit_isCaseInsensitiveEqualToString:@"application/x-webarchive"]; +} + +- (void)receivedData:(NSData *)data withDataSource:(WebDataSource *)dataSource +{ + WebFrame *webFrame = [dataSource webFrame]; + if (!webFrame) + return; + + if (!_private->pluginView) + [webFrame _commitData:data]; + + // If the document is a stand-alone media document, now is the right time to cancel the WebKit load + Frame* coreFrame = core(webFrame); + if (coreFrame->document()->isMediaDocument()) + coreFrame->loader()->documentLoader()->cancelMainResourceLoad(coreFrame->loader()->client()->pluginWillHandleLoadError(coreFrame->loader()->documentLoader()->response())); + + if (_private->pluginView) { + if (!_private->hasSentResponseToPlugin) { + [_private->manualLoader pluginView:_private->pluginView receivedResponse:[dataSource response]]; + _private->hasSentResponseToPlugin = YES; + } + + [_private->manualLoader pluginView:_private->pluginView receivedData:data]; + } +} + +- (void)receivedError:(NSError *)error withDataSource:(WebDataSource *)dataSource +{ + if (_private->pluginView) { + [_private->manualLoader pluginView:_private->pluginView receivedError:error]; + } +} + +- (void)finishedLoadingWithDataSource:(WebDataSource *)dataSource +{ + WebFrame* webFrame = [dataSource webFrame]; + + if (_private->pluginView) { + [_private->manualLoader pluginViewFinishedLoading:_private->pluginView]; + return; + } + + if (!webFrame) + return; + + if (![self _isDisplayingWebArchive]) { + // Telling the frame we received some data and passing nil as the data is our + // way to get work done that is normally done when the first bit of data is + // received, even for the case of a document with no data (like about:blank). + [webFrame _commitData:nil]; + } + + WebView *webView = [webFrame webView]; + if ([webView isEditable]) + core(webFrame)->editor()->applyEditingStyleToBodyElement(); +} + +- (BOOL)canProvideDocumentSource +{ + return [[_private->dataSource webFrame] _canProvideDocumentSource]; +} + +- (BOOL)canSaveAsWebArchive +{ + return [[_private->dataSource webFrame] _canSaveAsWebArchive]; +} + +- (NSString *)documentSource +{ + if ([self _isDisplayingWebArchive]) { + SharedBuffer *parsedArchiveData = [_private->dataSource _documentLoader]->parsedArchiveData(); + NSData *nsData = parsedArchiveData ? parsedArchiveData->createNSData() : nil; + NSString *result = [[NSString alloc] initWithData:nsData encoding:NSUTF8StringEncoding]; + [nsData release]; + return [result autorelease]; + } + + Frame* coreFrame = core([_private->dataSource webFrame]); + if (!coreFrame) + return nil; + Document* document = coreFrame->document(); + if (!document) + return nil; + TextResourceDecoder* decoder = document->decoder(); + if (!decoder) + return nil; + NSData *data = [_private->dataSource data]; + if (!data) + return nil; + return decoder->encoding().decode(reinterpret_cast<const char*>([data bytes]), [data length]); +} + +- (NSString *)title +{ + return nsStringNilIfEmpty([_private->dataSource _documentLoader]->title()); +} + +- (DOMDocument *)DOMDocument +{ + return [[_private->dataSource webFrame] DOMDocument]; +} + +- (NSAttributedString *)attributedText +{ + // FIXME: Implement + return nil; +} + +- (NSAttributedString *)attributedStringFrom:(DOMNode *)startNode startOffset:(int)startOffset to:(DOMNode *)endNode endOffset:(int)endOffset +{ + return [NSAttributedString _web_attributedStringFromRange:Range::create(core(startNode)->document(), core(startNode), startOffset, core(endNode), endOffset).get()]; +} + +static HTMLFormElement* formElementFromDOMElement(DOMElement *element) +{ + Element* node = core(element); + return node && node->hasTagName(formTag) ? static_cast<HTMLFormElement*>(node) : 0; +} + +- (DOMElement *)elementWithName:(NSString *)name inForm:(DOMElement *)form +{ + HTMLFormElement* formElement = formElementFromDOMElement(form); + if (!formElement) + return nil; + const Vector<FormAssociatedElement*>& elements = formElement->associatedElements(); + AtomicString targetName = name; + for (unsigned i = 0; i < elements.size(); i++) { + FormAssociatedElement* elt = elements[i]; + if (elt->name() == targetName) + return kit(toHTMLElement(elt)); + } + return nil; +} + +static HTMLInputElement* inputElementFromDOMElement(DOMElement* element) +{ + Element* node = core(element); + return node && node->hasTagName(inputTag) ? static_cast<HTMLInputElement*>(node) : 0; +} + +- (BOOL)elementDoesAutoComplete:(DOMElement *)element +{ + HTMLInputElement* inputElement = inputElementFromDOMElement(element); + return inputElement + && inputElement->isTextField() + && !inputElement->isPasswordField() + && inputElement->autoComplete(); +} + +- (BOOL)elementIsPassword:(DOMElement *)element +{ + HTMLInputElement* inputElement = inputElementFromDOMElement(element); + return inputElement && inputElement->isPasswordField(); +} + +- (DOMElement *)formForElement:(DOMElement *)element +{ + HTMLInputElement* inputElement = inputElementFromDOMElement(element); + return inputElement ? kit(inputElement->form()) : 0; +} + +- (DOMElement *)currentForm +{ + return kit(core([_private->dataSource webFrame])->selection()->currentForm()); +} + +- (NSArray *)controlsInForm:(DOMElement *)form +{ + HTMLFormElement* formElement = formElementFromDOMElement(form); + if (!formElement) + return nil; + NSMutableArray *results = nil; + const Vector<FormAssociatedElement*>& elements = formElement->associatedElements(); + for (unsigned i = 0; i < elements.size(); i++) { + if (elements[i]->isEnumeratable()) { // Skip option elements, other duds + DOMElement* de = kit(toHTMLElement(elements[i])); + if (!results) + results = [NSMutableArray arrayWithObject:de]; + else + [results addObject:de]; + } + } + return results; +} + +- (NSString *)searchForLabels:(NSArray *)labels beforeElement:(DOMElement *)element +{ + return [self searchForLabels:labels beforeElement:element resultDistance:0 resultIsInCellAbove:0]; +} + +- (NSString *)searchForLabels:(NSArray *)labels beforeElement:(DOMElement *)element resultDistance:(NSUInteger*)outDistance resultIsInCellAbove:(BOOL*)outIsInCellAbove +{ + size_t distance; + bool isInCellAbove; + + NSString *result = core([_private->dataSource webFrame])->searchForLabelsBeforeElement(labels, core(element), &distance, &isInCellAbove); + + if (outDistance) { + if (distance == notFound) + *outDistance = NSNotFound; + else + *outDistance = distance; + } + + if (outIsInCellAbove) + *outIsInCellAbove = isInCellAbove; + + return result; +} + +- (NSString *)matchLabels:(NSArray *)labels againstElement:(DOMElement *)element +{ + return core([_private->dataSource webFrame])->matchLabelsAgainstElement(labels, core(element)); +} + +@end diff --git a/Source/WebKit/mac/WebView/WebHTMLRepresentationPrivate.h b/Source/WebKit/mac/WebView/WebHTMLRepresentationPrivate.h new file mode 100644 index 0000000..d0c18ab --- /dev/null +++ b/Source/WebKit/mac/WebView/WebHTMLRepresentationPrivate.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * 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 <WebKit/WebHTMLRepresentation.h> + +@protocol WebPluginManualLoader; + +@interface WebHTMLRepresentation (WebPrivate) +- (void)_redirectDataToManualLoader:(id<WebPluginManualLoader>)manualLoader forPluginView:(NSView *)pluginView; +- (void)printDOMTree; +@end diff --git a/Source/WebKit/mac/WebView/WebHTMLView.h b/Source/WebKit/mac/WebView/WebHTMLView.h new file mode 100644 index 0000000..a191cef --- /dev/null +++ b/Source/WebKit/mac/WebView/WebHTMLView.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * 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 <WebKit/WebDocument.h> + +@class WebDataSource; +@class WebHTMLViewPrivate; + +/*! + @class WebHTMLView + @discussion A document view of WebFrameView that displays HTML content. + WebHTMLView is a NSControl because it hosts NSCells that are painted by WebCore's Aqua theme + renderer (and those cells must be hosted by an enclosing NSControl in order to paint properly). +*/ +@interface WebHTMLView : NSControl <WebDocumentView, WebDocumentSearching> +{ +@private + WebHTMLViewPrivate *_private; +} + +/*! + @method setNeedsToApplyStyles: + @abstract Sets flag to cause reapplication of style information. + @param flag YES to apply style information, NO to not apply style information. +*/ +- (void)setNeedsToApplyStyles:(BOOL)flag; + +/*! + @method reapplyStyles + @discussion Immediately causes reapplication of style information to the view. This should not be called directly, + instead call setNeedsToApplyStyles:. +*/ +- (void)reapplyStyles; + +- (void)outdent:(id)sender; + +@end + diff --git a/Source/WebKit/mac/WebView/WebHTMLView.mm b/Source/WebKit/mac/WebView/WebHTMLView.mm new file mode 100644 index 0000000..ec5ff51 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebHTMLView.mm @@ -0,0 +1,6338 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * (C) 2006, 2007 Graham Dennis (graham.dennis@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 "WebHTMLView.h" + +#import "DOMCSSStyleDeclarationInternal.h" +#import "DOMDocumentFragmentInternal.h" +#import "DOMDocumentInternal.h" +#import "DOMNodeInternal.h" +#import "DOMRangeInternal.h" +#import "WebArchive.h" +#import "WebClipView.h" +#import "WebDOMOperationsInternal.h" +#import "WebDataSourceInternal.h" +#import "WebDefaultUIDelegate.h" +#import "WebDelegateImplementationCaching.h" +#import "WebDocumentInternal.h" +#import "WebDynamicScrollBarsView.h" +#import "WebEditingDelegate.h" +#import "WebElementDictionary.h" +#import "WebFrameInternal.h" +#import "WebFramePrivate.h" +#import "WebFrameViewInternal.h" +#import "WebHTMLRepresentationPrivate.h" +#import "WebHTMLViewInternal.h" +#import "WebKitLogging.h" +#import "WebKitNSStringExtras.h" +#import "WebKitVersionChecks.h" +#import "WebLocalizableStrings.h" +#import "WebNSAttributedStringExtras.h" +#import "WebNSEventExtras.h" +#import "WebNSFileManagerExtras.h" +#import "WebNSImageExtras.h" +#import "WebNSObjectExtras.h" +#import "WebNSPasteboardExtras.h" +#import "WebNSPrintOperationExtras.h" +#import "WebNSURLExtras.h" +#import "WebNSViewExtras.h" +#import "WebNetscapePluginView.h" +#import "WebNodeHighlight.h" +#import "WebPluginController.h" +#import "WebPreferences.h" +#import "WebPreferencesPrivate.h" +#import "WebResourcePrivate.h" +#import "WebTextCompletionController.h" +#import "WebTypesInternal.h" +#import "WebUIDelegatePrivate.h" +#import "WebViewInternal.h" +#import <AppKit/NSAccessibility.h> +#import <ApplicationServices/ApplicationServices.h> +#import <WebCore/CSSMutableStyleDeclaration.h> +#import <WebCore/CachedImage.h> +#import <WebCore/CachedResourceClient.h> +#import <WebCore/ColorMac.h> +#import <WebCore/ContextMenu.h> +#import <WebCore/ContextMenuController.h> +#import <WebCore/Document.h> +#import <WebCore/DocumentFragment.h> +#import <WebCore/DocumentMarkerController.h> +#import <WebCore/DragController.h> +#import <WebCore/Editor.h> +#import <WebCore/EditorDeleteAction.h> +#import <WebCore/Element.h> +#import <WebCore/EventHandler.h> +#import <WebCore/ExceptionHandlers.h> +#import <WebCore/FloatRect.h> +#import <WebCore/FocusController.h> +#import <WebCore/Frame.h> +#import <WebCore/FrameLoader.h> +#import <WebCore/FrameView.h> +#import <WebCore/HTMLNames.h> +#import <WebCore/HitTestResult.h> +#import <WebCore/Image.h> +#import <WebCore/KeyboardEvent.h> +#import <WebCore/LegacyWebArchive.h> +#import <WebCore/MIMETypeRegistry.h> +#import <WebCore/Page.h> +#import <WebCore/PlatformKeyboardEvent.h> +#import <WebCore/Range.h> +#import <WebCore/RenderWidget.h> +#import <WebCore/RuntimeApplicationChecks.h> +#import <WebCore/SelectionController.h> +#import <WebCore/SharedBuffer.h> +#import <WebCore/SimpleFontData.h> +#import <WebCore/Text.h> +#import <WebCore/WebCoreObjCExtras.h> +#import <WebCore/WebFontCache.h> +#import <WebCore/markup.h> +#import <WebKit/DOM.h> +#import <WebKit/DOMExtensions.h> +#import <WebKit/DOMPrivate.h> +#import <WebKitSystemInterface.h> +#import <dlfcn.h> +#import <limits> +#import <runtime/InitializeThreading.h> +#import <wtf/Threading.h> + +#if USE(ACCELERATED_COMPOSITING) +#import <QuartzCore/QuartzCore.h> +#endif + +using namespace WebCore; +using namespace HTMLNames; +using namespace WTF; +using namespace std; + +@interface WebMenuTarget : NSObject { + WebCore::ContextMenuController* _menuController; +} ++ (WebMenuTarget*)sharedMenuTarget; +- (WebCore::ContextMenuController*)menuController; +- (void)setMenuController:(WebCore::ContextMenuController*)menuController; +- (void)forwardContextMenuAction:(id)sender; +- (BOOL)validateMenuItem:(NSMenuItem *)item; +@end + +static WebMenuTarget* target; + +@implementation WebMenuTarget + ++ (WebMenuTarget*)sharedMenuTarget +{ + if (!target) + target = [[WebMenuTarget alloc] init]; + return target; +} + +- (WebCore::ContextMenuController*)menuController +{ + return _menuController; +} + +- (void)setMenuController:(WebCore::ContextMenuController*)menuController +{ + _menuController = menuController; +} + +- (void)forwardContextMenuAction:(id)sender +{ + WebCore::ContextMenuItem item(WebCore::ActionType, static_cast<WebCore::ContextMenuAction>([sender tag]), [sender title]); + _menuController->contextMenuItemSelected(&item); +} + +- (BOOL)validateMenuItem:(NSMenuItem *)item +{ + WebCore::ContextMenuItem coreItem(item); + ASSERT(_menuController->contextMenu()); + _menuController->checkOrEnableIfNeeded(coreItem); + return coreItem.enabled(); +} + +@end + +@interface NSWindow (BorderViewAccess) +- (NSView*)_web_borderView; +@end + +@implementation NSWindow (BorderViewAccess) +- (NSView*)_web_borderView +{ + return _borderView; +} +@end + +@interface WebResponderChainSink : NSResponder { + NSResponder* _lastResponderInChain; + BOOL _receivedUnhandledCommand; +} +- (id)initWithResponderChain:(NSResponder *)chain; +- (void)detach; +- (BOOL)receivedUnhandledCommand; +@end + +// if YES, do the standard NSView hit test (which can't give the right result when HTML overlaps a view) +static BOOL forceNSViewHitTest; + +// if YES, do the "top WebHTMLView" hit test (which we'd like to do all the time but can't because of Java requirements [see bug 4349721]) +static BOOL forceWebHTMLViewHitTest; + +static WebHTMLView *lastHitView; + +static bool needsCursorRectsSupportAtPoint(NSWindow* window, NSPoint point) +{ + forceNSViewHitTest = YES; + NSView* view = [[window _web_borderView] hitTest:point]; + forceNSViewHitTest = NO; + + // WebHTMLView doesn't use cursor rects. + if ([view isKindOfClass:[WebHTMLView class]]) + return false; + +#if ENABLE(NETSCAPE_PLUGIN_API) + // Neither do NPAPI plug-ins. + if ([view isKindOfClass:[WebBaseNetscapePluginView class]]) + return false; +#endif + + // Non-Web content, WebPDFView, and WebKit plug-ins use normal cursor handling. + return true; +} + +#ifndef BUILDING_ON_TIGER + +static IMP oldSetCursorForMouseLocationIMP; + +// Overriding an internal method is a hack; <rdar://problem/7662987> tracks finding a better solution. +static void setCursor(NSWindow *self, SEL cmd, NSPoint point) +{ + if (needsCursorRectsSupportAtPoint(self, point)) + oldSetCursorForMouseLocationIMP(self, cmd, point); +} + +#else + +static IMP oldResetCursorRectsIMP; +static IMP oldSetCursorIMP; +static BOOL canSetCursor = YES; + +static void resetCursorRects(NSWindow* self, SEL cmd) +{ + canSetCursor = needsCursorRectsSupportAtPoint(self, [self mouseLocationOutsideOfEventStream]); + oldResetCursorRectsIMP(self, cmd); + canSetCursor = YES; +} + +static void setCursor(NSCursor* self, SEL cmd) +{ + if (canSetCursor) + oldSetCursorIMP(self, cmd); +} + +#endif + +extern "C" { + +// Need to declare these attribute names because AppKit exports them but does not make them available in API or SPI headers. + +extern NSString *NSMarkedClauseSegmentAttributeName; +extern NSString *NSTextInputReplacementRangeAttributeName; + +} + +@interface NSView (WebNSViewDetails) +- (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView; +- (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect; +- (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView; +- (NSRect)_dirtyRect; +- (void)_setDrawsOwnDescendants:(BOOL)drawsOwnDescendants; +- (BOOL)_drawnByAncestor; +- (void)_invalidateGStatesForTree; +- (void)_propagateDirtyRectsToOpaqueAncestors; +- (void)_windowChangedKeyState; +#if USE(ACCELERATED_COMPOSITING) && defined(BUILDING_ON_LEOPARD) +- (void)_updateLayerGeometryFromView; +#endif +@end + +#if USE(ACCELERATED_COMPOSITING) +static IMP oldSetNeedsDisplayInRectIMP; + +static void setNeedsDisplayInRect(NSView *self, SEL cmd, NSRect invalidRect) +{ + if (![self _drawnByAncestor]) { + oldSetNeedsDisplayInRectIMP(self, cmd, invalidRect); + return; + } + + static Class webFrameViewClass = [WebFrameView class]; + WebFrameView *enclosingWebFrameView = (WebFrameView *)self; + while (enclosingWebFrameView && ![enclosingWebFrameView isKindOfClass:webFrameViewClass]) + enclosingWebFrameView = (WebFrameView *)[enclosingWebFrameView superview]; + + if (!enclosingWebFrameView) { + oldSetNeedsDisplayInRectIMP(self, cmd, invalidRect); + return; + } + + Frame* coreFrame = core([enclosingWebFrameView webFrame]); + FrameView* frameView = coreFrame ? coreFrame->view() : 0; + if (!frameView || !frameView->isEnclosedInCompositingLayer()) { + oldSetNeedsDisplayInRectIMP(self, cmd, invalidRect); + return; + } + + NSRect invalidRectInWebFrameViewCoordinates = [enclosingWebFrameView convertRect:invalidRect fromView:self]; + IntRect invalidRectInFrameViewCoordinates(invalidRectInWebFrameViewCoordinates); + if (![enclosingWebFrameView isFlipped]) + invalidRectInFrameViewCoordinates.setY(frameView->frameRect().size().height() - invalidRectInFrameViewCoordinates.bottom()); + + frameView->invalidateRect(invalidRectInFrameViewCoordinates); +} +#endif // USE(ACCELERATED_COMPOSITING) + +@interface NSApplication (WebNSApplicationDetails) +- (void)speakString:(NSString *)string; +@end + +@interface NSWindow (WebNSWindowDetails) +- (id)_newFirstResponderAfterResigning; +@end + +@interface NSAttributedString (WebNSAttributedStringDetails) +- (id)_initWithDOMRange:(DOMRange *)range; +- (DOMDocumentFragment *)_documentFromRange:(NSRange)range document:(DOMDocument *)document documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources; +@end + +@interface NSSpellChecker (WebNSSpellCheckerDetails) +- (void)learnWord:(NSString *)word; +@end + +// By imaging to a width a little wider than the available pixels, +// thin pages will be scaled down a little, matching the way they +// print in IE and Camino. This lets them use fewer sheets than they +// would otherwise, which is presumably why other browsers do this. +// Wide pages will be scaled down more than this. +const float _WebHTMLViewPrintingMinimumShrinkFactor = 1.25; + +// This number determines how small we are willing to reduce the page content +// in order to accommodate the widest line. If the page would have to be +// reduced smaller to make the widest line fit, we just clip instead (this +// behavior matches MacIE and Mozilla, at least) +const float _WebHTMLViewPrintingMaximumShrinkFactor = 2; + +// This number determines how short the last printed page of a multi-page print session +// can be before we try to shrink the scale in order to reduce the number of pages, and +// thus eliminate the orphan. +#define LastPrintedPageOrphanRatio 0.1f + +// This number determines the amount the scale factor is adjusted to try to eliminate orphans. +// It has no direct mathematical relationship to LastPrintedPageOrphanRatio, due to variable +// numbers of pages, logic to avoid breaking elements, and CSS-supplied hard page breaks. +#define PrintingOrphanShrinkAdjustment 1.1f + +#define AUTOSCROLL_INTERVAL 0.1f + +// Any non-zero value will do, but using something recognizable might help us debug some day. +#define TRACKING_RECT_TAG 0xBADFACE + +// FIXME: This constant is copied from AppKit's _NXSmartPaste constant. +#define WebSmartPastePboardType @"NeXT smart paste pasteboard type" + +#define STANDARD_WEIGHT 5 +#define MIN_BOLD_WEIGHT 7 +#define STANDARD_BOLD_WEIGHT 9 + +// Fake URL scheme. +#define WebDataProtocolScheme @"webkit-fake-url" + +// <rdar://problem/4985524> References to WebCoreScrollView as a subview of a WebHTMLView may be present +// in some NIB files, so NSUnarchiver must be still able to look up this now-unused class. +@interface WebCoreScrollView : NSScrollView +@end + +@implementation WebCoreScrollView +@end + +// We need this to be able to safely reference the CachedImage for the promised drag data +static CachedResourceClient* promisedDataClient() +{ + static CachedResourceClient* staticCachedResourceClient = new CachedResourceClient; + return staticCachedResourceClient; +} + +@interface WebHTMLView (WebHTMLViewFileInternal) +- (BOOL)_imageExistsAtPaths:(NSArray *)paths; +- (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard inContext:(DOMRange *)context allowPlainText:(BOOL)allowPlainText; +- (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard; +- (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText; +- (void)_removeMouseMovedObserverUnconditionally; +- (void)_removeSuperviewObservers; +- (void)_removeWindowObservers; +- (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action; +- (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action; +- (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action; +- (DOMRange *)_selectedRange; +- (BOOL)_shouldDeleteRange:(DOMRange *)range; +- (NSView *)_hitViewForEvent:(NSEvent *)event; +- (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString; +- (DOMRange *)_documentRange; +- (void)_setMouseDownEvent:(NSEvent *)event; +- (WebHTMLView *)_topHTMLView; +- (BOOL)_isTopHTMLView; +- (void)_web_setPrintingModeRecursive; +- (void)_web_setPrintingModeRecursiveAndAdjustViewSize; +- (void)_web_clearPrintingModeRecursive; +@end + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + +@interface WebHTMLView (WebHTMLViewTextCheckingInternal) +- (void)orderFrontSubstitutionsPanel:(id)sender; +- (BOOL)smartInsertDeleteEnabled; +- (void)setSmartInsertDeleteEnabled:(BOOL)flag; +- (void)toggleSmartInsertDelete:(id)sender; +- (BOOL)isAutomaticQuoteSubstitutionEnabled; +- (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag; +- (void)toggleAutomaticQuoteSubstitution:(id)sender; +- (BOOL)isAutomaticLinkDetectionEnabled; +- (void)setAutomaticLinkDetectionEnabled:(BOOL)flag; +- (void)toggleAutomaticLinkDetection:(id)sender; +- (BOOL)isAutomaticDashSubstitutionEnabled; +- (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag; +- (void)toggleAutomaticDashSubstitution:(id)sender; +- (BOOL)isAutomaticTextReplacementEnabled; +- (void)setAutomaticTextReplacementEnabled:(BOOL)flag; +- (void)toggleAutomaticTextReplacement:(id)sender; +- (BOOL)isAutomaticSpellingCorrectionEnabled; +- (void)setAutomaticSpellingCorrectionEnabled:(BOOL)flag; +- (void)toggleAutomaticSpellingCorrection:(id)sender; +@end + +#endif + +@interface WebHTMLView (WebForwardDeclaration) // FIXME: Put this in a normal category and stop doing the forward declaration trick. +- (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth height:(float)minPageHeight maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize paginateScreenContent:(BOOL)paginateScreenContent; +@end + +@class NSTextInputContext; +@interface NSResponder (AppKitDetails) +- (NSTextInputContext *)inputContext; +@end + +@interface NSObject (NSTextInputContextDetails) +- (BOOL)wantsToHandleMouseEvents; +- (BOOL)handleMouseEvent:(NSEvent *)event; +@end + +@interface WebHTMLView (WebNSTextInputSupport) <NSTextInput> +- (void)_updateSelectionForInputManager; +@end + +@interface WebHTMLView (WebEditingStyleSupport) +- (DOMCSSStyleDeclaration *)_emptyStyle; +- (NSString *)_colorAsString:(NSColor *)color; +@end + +@interface NSView (WebHTMLViewFileInternal) +- (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *) array; +@end + +@interface NSMutableDictionary (WebHTMLViewFileInternal) +- (void)_web_setObjectIfNotNil:(id)object forKey:(id)key; +@end + +struct WebHTMLViewInterpretKeyEventsParameters { + KeyboardEvent* event; + BOOL eventWasHandled; + BOOL shouldSaveCommand; + // The Input Method may consume an event and not tell us, in + // which case we should not bubble the event up the DOM + BOOL consumedByIM; +}; + +@interface WebHTMLViewPrivate : NSObject { +@public + BOOL closed; + BOOL ignoringMouseDraggedEvents; + BOOL printing; + BOOL avoidingPrintOrphan; + BOOL paginateScreenContent; + BOOL observingMouseMovedNotifications; + BOOL observingSuperviewNotifications; + BOOL observingWindowNotifications; + + id savedSubviews; + BOOL subviewsSetAside; + +#if USE(ACCELERATED_COMPOSITING) + NSView *layerHostingView; + BOOL drawingIntoLayer; +#endif + + NSEvent *mouseDownEvent; // Kept after handling the event. + BOOL handlingMouseDownEvent; + NSEvent *keyDownEvent; // Kept after handling the event. + + // A WebHTMLView has a single input context, but we return nil when in non-editable content to avoid making input methods do their work. + // This state is saved each time selection changes, because computing it causes style recalc, which is not always safe to do. + BOOL exposeInputContext; + + NSPoint lastScrollPosition; +#ifndef BUILDING_ON_TIGER + BOOL inScrollPositionChanged; +#endif + + WebPluginController *pluginController; + + NSString *toolTip; + NSToolTipTag lastToolTipTag; + id trackingRectOwner; + void *trackingRectUserData; + + NSTimer *autoscrollTimer; + NSEvent *autoscrollTriggerEvent; + + NSArray *pageRects; + + NSMutableDictionary *highlighters; + +#ifdef BUILDING_ON_TIGER + BOOL nextResponderDisabledOnce; +#endif + + WebTextCompletionController *completionController; + + BOOL transparentBackground; + + WebHTMLViewInterpretKeyEventsParameters* interpretKeyEventsParameters; + BOOL receivedNOOP; + + WebDataSource *dataSource; + WebCore::CachedImage* promisedDragTIFFDataSource; + + CFRunLoopTimerRef updateMouseoverTimer; + + SEL selectorForDoCommandBySelector; + +#ifndef NDEBUG + BOOL enumeratingSubviews; +#endif +} +- (void)clear; +@end + +static NSCellStateValue kit(TriState state) +{ + switch (state) { + case FalseTriState: + return NSOffState; + case TrueTriState: + return NSOnState; + case MixedTriState: + return NSMixedState; + } + ASSERT_NOT_REACHED(); + return NSOffState; +} + +static FindOptions coreOptions(WebFindOptions options) +{ + return (options & WebFindOptionsCaseInsensitive ? CaseInsensitive : 0) + | (options & WebFindOptionsAtWordStarts ? AtWordStarts : 0) + | (options & WebFindOptionsTreatMedialCapitalAsWordStart ? TreatMedialCapitalAsWordStart : 0) + | (options & WebFindOptionsBackwards ? Backwards : 0) + | (options & WebFindOptionsWrapAround ? WrapAround : 0) + | (options & WebFindOptionsStartInSelection ? StartInSelection : 0); +} + +@implementation WebHTMLViewPrivate + ++ (void)initialize +{ + JSC::initializeThreading(); + WTF::initializeMainThreadToProcessMainThread(); +#ifndef BUILDING_ON_TIGER + WebCoreObjCFinalizeOnMainThread(self); +#endif + +#ifndef BUILDING_ON_TIGER + if (!oldSetCursorForMouseLocationIMP) { + Method setCursorMethod = class_getInstanceMethod([NSWindow class], @selector(_setCursorForMouseLocation:)); + ASSERT(setCursorMethod); + oldSetCursorForMouseLocationIMP = method_setImplementation(setCursorMethod, (IMP)setCursor); + ASSERT(oldSetCursorForMouseLocationIMP); + } + +#if USE(ACCELERATED_COMPOSITING) + if (!oldSetNeedsDisplayInRectIMP) { + Method setNeedsDisplayInRectMethod = class_getInstanceMethod([NSView class], @selector(setNeedsDisplayInRect:)); + ASSERT(setNeedsDisplayInRectMethod); + oldSetNeedsDisplayInRectIMP = method_setImplementation(setNeedsDisplayInRectMethod, (IMP)setNeedsDisplayInRect); + ASSERT(oldSetNeedsDisplayInRectIMP); + } +#endif // USE(ACCELERATED_COMPOSITING) + +#else // defined(BUILDING_ON_TIGER) + if (!oldSetCursorIMP) { + Method setCursorMethod = class_getInstanceMethod([NSCursor class], @selector(set)); + ASSERT(setCursorMethod); + oldSetCursorIMP = method_setImplementation(setCursorMethod, (IMP)setCursor); + ASSERT(oldSetCursorIMP); + } + if (!oldResetCursorRectsIMP) { + Method resetCursorRectsMethod = class_getInstanceMethod([NSWindow class], @selector(resetCursorRects)); + ASSERT(resetCursorRectsMethod); + oldResetCursorRectsIMP = method_setImplementation(resetCursorRectsMethod, (IMP)resetCursorRects); + ASSERT(oldResetCursorRectsIMP); + } +#endif + +} + +- (void)dealloc +{ + if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLViewPrivate class], self)) + return; + + ASSERT(!autoscrollTimer); + ASSERT(!autoscrollTriggerEvent); + ASSERT(!updateMouseoverTimer); + + [mouseDownEvent release]; + [keyDownEvent release]; + [pluginController release]; + [toolTip release]; + [completionController release]; + [dataSource release]; + [highlighters release]; + if (promisedDragTIFFDataSource) + promisedDragTIFFDataSource->removeClient(promisedDataClient()); + + [super dealloc]; +} + +- (void)finalize +{ + ASSERT_MAIN_THREAD(); + + if (promisedDragTIFFDataSource) + promisedDragTIFFDataSource->removeClient(promisedDataClient()); + + [super finalize]; +} + +- (void)clear +{ + [mouseDownEvent release]; + [keyDownEvent release]; + [pluginController release]; + [toolTip release]; + [completionController release]; + [dataSource release]; + [highlighters release]; + if (promisedDragTIFFDataSource) + promisedDragTIFFDataSource->removeClient(promisedDataClient()); + + mouseDownEvent = nil; + keyDownEvent = nil; + pluginController = nil; + toolTip = nil; + completionController = nil; + dataSource = nil; + highlighters = nil; + promisedDragTIFFDataSource = 0; + +#if USE(ACCELERATED_COMPOSITING) + layerHostingView = nil; +#endif +} + +@end + +@implementation WebHTMLView (WebHTMLViewFileInternal) + +- (DOMRange *)_documentRange +{ + return [[[self _frame] DOMDocument] _documentRange]; +} + +- (BOOL)_imageExistsAtPaths:(NSArray *)paths +{ + NSEnumerator *enumerator = [paths objectEnumerator]; + NSString *path; + + while ((path = [enumerator nextObject]) != nil) { + NSString *MIMEType = WKGetMIMETypeForExtension([path pathExtension]); + if (MIMETypeRegistry::isSupportedImageResourceMIMEType(MIMEType)) + return YES; + } + + return NO; +} + +- (WebDataSource *)_dataSource +{ + return _private->dataSource; +} + +- (WebView *)_webView +{ + return [_private->dataSource _webView]; +} + +- (WebFrameView *)_frameView +{ + return [[_private->dataSource webFrame] frameView]; +} + +- (DOMDocumentFragment *)_documentFragmentWithPaths:(NSArray *)paths +{ + DOMDocumentFragment *fragment; + NSEnumerator *enumerator = [paths objectEnumerator]; + NSMutableArray *domNodes = [[NSMutableArray alloc] init]; + NSString *path; + + while ((path = [enumerator nextObject]) != nil) { + // Non-image file types; _web_userVisibleString is appropriate here because this will + // be pasted as visible text. + NSString *url = [[[NSURL fileURLWithPath:path] _webkit_canonicalize] _web_userVisibleString]; + [domNodes addObject:[[[self _frame] DOMDocument] createTextNode: url]]; + } + + fragment = [[self _frame] _documentFragmentWithNodesAsParagraphs:domNodes]; + + [domNodes release]; + + return [fragment firstChild] != nil ? fragment : nil; +} + ++ (NSArray *)_excludedElementsForAttributedStringConversion +{ + static NSArray *elements = nil; + if (elements == nil) { + elements = [[NSArray alloc] initWithObjects: + // Omit style since we want style to be inline so the fragment can be easily inserted. + @"style", + // Omit xml so the result is not XHTML. + @"xml", + // Omit tags that will get stripped when converted to a fragment anyway. + @"doctype", @"html", @"head", @"body", + // Omit deprecated tags. + @"applet", @"basefont", @"center", @"dir", @"font", @"isindex", @"menu", @"s", @"strike", @"u", + // Omit object so no file attachments are part of the fragment. + @"object", nil]; + CFRetain(elements); + } + return elements; +} + +static NSURL* uniqueURLWithRelativePart(NSString *relativePart) +{ + CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault); + NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef); + CFRelease(UUIDRef); + NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@/%@", WebDataProtocolScheme, UUIDString, relativePart]]; + CFRelease(UUIDString); + + return URL; +} + +- (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard + inContext:(DOMRange *)context + allowPlainText:(BOOL)allowPlainText +{ + NSArray *types = [pasteboard types]; + DOMDocumentFragment *fragment = nil; + + if ([types containsObject:WebArchivePboardType] && + (fragment = [self _documentFragmentFromPasteboard:pasteboard + forType:WebArchivePboardType + inContext:context + subresources:0])) + return fragment; + + if ([types containsObject:NSFilenamesPboardType] && + (fragment = [self _documentFragmentFromPasteboard:pasteboard + forType:NSFilenamesPboardType + inContext:context + subresources:0])) + return fragment; + + if ([types containsObject:NSHTMLPboardType] && + (fragment = [self _documentFragmentFromPasteboard:pasteboard + forType:NSHTMLPboardType + inContext:context + subresources:0])) + return fragment; + + if ([types containsObject:NSRTFDPboardType] && + (fragment = [self _documentFragmentFromPasteboard:pasteboard + forType:NSRTFDPboardType + inContext:context + subresources:0])) + return fragment; + + if ([types containsObject:NSRTFPboardType] && + (fragment = [self _documentFragmentFromPasteboard:pasteboard + forType:NSRTFPboardType + inContext:context + subresources:0])) + return fragment; + + if ([types containsObject:NSTIFFPboardType] && + (fragment = [self _documentFragmentFromPasteboard:pasteboard + forType:NSTIFFPboardType + inContext:context + subresources:0])) + return fragment; + + if ([types containsObject:NSPDFPboardType] && + (fragment = [self _documentFragmentFromPasteboard:pasteboard + forType:NSPDFPboardType + inContext:context + subresources:0])) + return fragment; + +#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) + if ([types containsObject:NSPICTPboardType] && + (fragment = [self _documentFragmentFromPasteboard:pasteboard + forType:NSPICTPboardType + inContext:context + subresources:0])) + return fragment; +#endif + + // Only 10.5 and higher support setting and retrieving pasteboard types with UTIs, but we don't believe + // that any applications on Tiger put types for which we only have a UTI, like PNG, on the pasteboard. + if ([types containsObject:(NSString*)kUTTypePNG] && + (fragment = [self _documentFragmentFromPasteboard:pasteboard + forType:(NSString*)kUTTypePNG + inContext:context + subresources:0])) + return fragment; + + if ([types containsObject:NSURLPboardType] && + (fragment = [self _documentFragmentFromPasteboard:pasteboard + forType:NSURLPboardType + inContext:context + subresources:0])) + return fragment; + + if (allowPlainText && [types containsObject:NSStringPboardType] && + (fragment = [self _documentFragmentFromPasteboard:pasteboard + forType:NSStringPboardType + inContext:context + subresources:0])) { + return fragment; + } + + return nil; +} + +- (NSString *)_plainTextFromPasteboard:(NSPasteboard *)pasteboard +{ + NSArray *types = [pasteboard types]; + + if ([types containsObject:NSStringPboardType]) + return [[pasteboard stringForType:NSStringPboardType] precomposedStringWithCanonicalMapping]; + + NSAttributedString *attributedString = nil; + NSString *string; + + if ([types containsObject:NSRTFDPboardType]) + attributedString = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL]; + if (attributedString == nil && [types containsObject:NSRTFPboardType]) + attributedString = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL]; + if (attributedString != nil) { + string = [[attributedString string] copy]; + [attributedString release]; + return [string autorelease]; + } + + if ([types containsObject:NSFilenamesPboardType]) { + string = [[pasteboard propertyListForType:NSFilenamesPboardType] componentsJoinedByString:@"\n"]; + if (string != nil) + return string; + } + + NSURL *URL; + + if ((URL = [NSURL URLFromPasteboard:pasteboard])) { + string = [URL _web_userVisibleString]; + if ([string length] > 0) + return string; + } + + return nil; +} + +- (void)_pasteWithPasteboard:(NSPasteboard *)pasteboard allowPlainText:(BOOL)allowPlainText +{ + WebView *webView = [[self _webView] retain]; + [webView _setInsertionPasteboard:pasteboard]; + + DOMRange *range = [self _selectedRange]; + Frame* coreFrame = core([self _frame]); + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) + DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText]; + if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:range givenAction:WebViewInsertActionPasted]) + coreFrame->editor()->pasteAsFragment(core(fragment), [self _canSmartReplaceWithPasteboard:pasteboard], false); +#else + // Mail is ignoring the frament passed to the delegate and creates a new one. + // We want to avoid creating the fragment twice. + if (applicationIsAppleMail()) { + if ([self _shouldInsertFragment:nil replacingDOMRange:range givenAction:WebViewInsertActionPasted]) { + DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText]; + if (fragment) + coreFrame->editor()->pasteAsFragment(core(fragment), [self _canSmartReplaceWithPasteboard:pasteboard], false); + } + } else { + DOMDocumentFragment *fragment = [self _documentFragmentFromPasteboard:pasteboard inContext:range allowPlainText:allowPlainText]; + if (fragment && [self _shouldInsertFragment:fragment replacingDOMRange:range givenAction:WebViewInsertActionPasted]) + coreFrame->editor()->pasteAsFragment(core(fragment), [self _canSmartReplaceWithPasteboard:pasteboard], false); + } +#endif + [webView _setInsertionPasteboard:nil]; + [webView release]; +} + +- (void)_removeMouseMovedObserverUnconditionally +{ + if (!_private || !_private->observingMouseMovedNotifications) + return; + + [[NSNotificationCenter defaultCenter] removeObserver:self name:WKMouseMovedNotification() object:nil]; + _private->observingMouseMovedNotifications = false; +} + +- (void)_removeSuperviewObservers +{ + if (!_private || !_private->observingSuperviewNotifications) + return; + + NSView *superview = [self superview]; + if (!superview || ![self window]) + return; + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter removeObserver:self name:NSViewFrameDidChangeNotification object:superview]; + [notificationCenter removeObserver:self name:NSViewBoundsDidChangeNotification object:superview]; + + _private->observingSuperviewNotifications = false; +} + +- (void)_removeWindowObservers +{ + if (!_private->observingWindowNotifications) + return; + + NSWindow *window = [self window]; + if (!window) + return; + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil]; + [notificationCenter removeObserver:self name:NSWindowDidResignKeyNotification object:nil]; + [notificationCenter removeObserver:self name:NSWindowWillCloseNotification object:window]; + + _private->observingWindowNotifications = false; +} + +- (BOOL)_shouldInsertFragment:(DOMDocumentFragment *)fragment replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action +{ + WebView *webView = [self _webView]; + DOMNode *child = [fragment firstChild]; + if ([fragment lastChild] == child && [child isKindOfClass:[DOMCharacterData class]]) + return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:[(DOMCharacterData *)child data] replacingDOMRange:range givenAction:action]; + return [[webView _editingDelegateForwarder] webView:webView shouldInsertNode:fragment replacingDOMRange:range givenAction:action]; +} + +- (BOOL)_shouldInsertText:(NSString *)text replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action +{ + WebView *webView = [self _webView]; + return [[webView _editingDelegateForwarder] webView:webView shouldInsertText:text replacingDOMRange:range givenAction:action]; +} + +- (BOOL)_shouldReplaceSelectionWithText:(NSString *)text givenAction:(WebViewInsertAction)action +{ + return [self _shouldInsertText:text replacingDOMRange:[self _selectedRange] givenAction:action]; +} + +- (DOMRange *)_selectedRange +{ + Frame* coreFrame = core([self _frame]); + return coreFrame ? kit(coreFrame->selection()->toNormalizedRange().get()) : nil; +} + +- (BOOL)_shouldDeleteRange:(DOMRange *)range +{ + Frame* coreFrame = core([self _frame]); + return coreFrame && coreFrame->editor()->shouldDeleteRange(core(range)); +} + +- (NSView *)_hitViewForEvent:(NSEvent *)event +{ + // Usually, we hack AK's hitTest method to catch all events at the topmost WebHTMLView. + // Callers of this method, however, want to query the deepest view instead. + forceNSViewHitTest = YES; + NSView *hitView = [[[self window] contentView] hitTest:[event locationInWindow]]; + forceNSViewHitTest = NO; + return hitView; +} + +- (void)_writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard cachedAttributedString:(NSAttributedString *)attributedString +{ + // Put HTML on the pasteboard. + if ([types containsObject:WebArchivePboardType]) { + if (RefPtr<LegacyWebArchive> coreArchive = LegacyWebArchive::createFromSelection(core([self _frame]))) { + if (RetainPtr<CFDataRef> data = coreArchive ? coreArchive->rawDataRepresentation() : 0) + [pasteboard setData:(NSData *)data.get() forType:WebArchivePboardType]; + } + } + + // Put the attributed string on the pasteboard (RTF/RTFD format). + if ([types containsObject:NSRTFDPboardType]) { + if (attributedString == nil) { + attributedString = [self selectedAttributedString]; + } + NSData *RTFDData = [attributedString RTFDFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil]; + [pasteboard setData:RTFDData forType:NSRTFDPboardType]; + } + if ([types containsObject:NSRTFPboardType]) { + if (attributedString == nil) { + attributedString = [self selectedAttributedString]; + } + if ([attributedString containsAttachments]) { + attributedString = [attributedString _web_attributedStringByStrippingAttachmentCharacters]; + } + NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil]; + [pasteboard setData:RTFData forType:NSRTFPboardType]; + } + + // Put plain string on the pasteboard. + if ([types containsObject:NSStringPboardType]) { + // Map to a plain old space because this is better for source code, other browsers do it, + // and because HTML forces you to do this any time you want two spaces in a row. + NSMutableString *s = [[self 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]; + } + + if ([self _canSmartCopyOrDelete] && [types containsObject:WebSmartPastePboardType]) { + [pasteboard setData:nil forType:WebSmartPastePboardType]; + } +} + +- (void)_setMouseDownEvent:(NSEvent *)event +{ + ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown); + + if (event == _private->mouseDownEvent) + return; + + [event retain]; + [_private->mouseDownEvent release]; + _private->mouseDownEvent = event; +} + +- (void)_cancelUpdateMouseoverTimer +{ + if (_private->updateMouseoverTimer) { + CFRunLoopTimerInvalidate(_private->updateMouseoverTimer); + CFRelease(_private->updateMouseoverTimer); + _private->updateMouseoverTimer = NULL; + } +} + +- (WebHTMLView *)_topHTMLView +{ + // FIXME: this can fail if the dataSource is nil, which happens when the WebView is tearing down from the window closing. + WebHTMLView *view = (WebHTMLView *)[[[[_private->dataSource _webView] mainFrame] frameView] documentView]; + ASSERT(!view || [view isKindOfClass:[WebHTMLView class]]); + return view; +} + +- (BOOL)_isTopHTMLView +{ + // FIXME: this should be a cached boolean that doesn't rely on _topHTMLView since that can fail (see _topHTMLView). + return self == [self _topHTMLView]; +} + +- (void)_web_setPrintingModeRecursive +{ + [self _setPrinting:YES minimumPageWidth:0 height:0 maximumPageWidth:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]]; + +#ifndef NDEBUG + _private->enumeratingSubviews = YES; +#endif + + NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init]; + + [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews]; + + unsigned count = [descendantWebHTMLViews count]; + for (unsigned i = 0; i < count; ++i) + [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageWidth:0 height:0 maximumPageWidth:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]]; + + [descendantWebHTMLViews release]; + +#ifndef NDEBUG + _private->enumeratingSubviews = NO; +#endif +} + +- (void)_web_clearPrintingModeRecursive +{ + [self _setPrinting:NO minimumPageWidth:0 height:0 maximumPageWidth:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]]; + +#ifndef NDEBUG + _private->enumeratingSubviews = YES; +#endif + + NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init]; + + [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews]; + + unsigned count = [descendantWebHTMLViews count]; + for (unsigned i = 0; i < count; ++i) + [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:NO minimumPageWidth:0 height:0 maximumPageWidth:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]]; + + [descendantWebHTMLViews release]; + +#ifndef NDEBUG + _private->enumeratingSubviews = NO; +#endif +} + +- (void)_web_setPrintingModeRecursiveAndAdjustViewSize +{ + [self _setPrinting:YES minimumPageWidth:0 height:0 maximumPageWidth:0 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]]; + +#ifndef NDEBUG + _private->enumeratingSubviews = YES; +#endif + + NSMutableArray *descendantWebHTMLViews = [[NSMutableArray alloc] init]; + + [self _web_addDescendantWebHTMLViewsToArray:descendantWebHTMLViews]; + + unsigned count = [descendantWebHTMLViews count]; + for (unsigned i = 0; i < count; ++i) + [[descendantWebHTMLViews objectAtIndex:i] _setPrinting:YES minimumPageWidth:0 height:0 maximumPageWidth:0 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]]; + + [descendantWebHTMLViews release]; + +#ifndef NDEBUG + _private->enumeratingSubviews = NO; +#endif +} + +@end + +@implementation WebHTMLView (WebPrivate) + ++ (NSArray *)supportedMIMETypes +{ + return [WebHTMLRepresentation supportedMIMETypes]; +} + ++ (NSArray *)supportedImageMIMETypes +{ + return [WebHTMLRepresentation supportedImageMIMETypes]; +} + ++ (NSArray *)supportedNonImageMIMETypes +{ + return [WebHTMLRepresentation supportedNonImageMIMETypes]; +} + ++ (NSArray *)unsupportedTextMIMETypes +{ + return [NSArray arrayWithObjects: + @"text/calendar", // iCal + @"text/x-calendar", + @"text/x-vcalendar", + @"text/vcalendar", + @"text/vcard", // vCard + @"text/x-vcard", + @"text/directory", + @"text/ldif", // Netscape Address Book + @"text/qif", // Quicken + @"text/x-qif", + @"text/x-csv", // CSV (for Address Book and Microsoft Outlook) + @"text/x-vcf", // vCard type used in Sun affinity app + @"text/rtf", // Rich Text Format + nil]; +} + ++ (void)_postFlagsChangedEvent:(NSEvent *)flagsChangedEvent +{ + // This is a workaround for: <rdar://problem/2981619> NSResponder_Private should include notification for FlagsChanged + NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved + location:[[flagsChangedEvent window] convertScreenToBase:[NSEvent mouseLocation]] + modifierFlags:[flagsChangedEvent modifierFlags] + timestamp:[flagsChangedEvent timestamp] + windowNumber:[flagsChangedEvent windowNumber] + context:[flagsChangedEvent context] + eventNumber:0 clickCount:0 pressure:0]; + + // Pretend it's a mouse move. + [[NSNotificationCenter defaultCenter] + postNotificationName:WKMouseMovedNotification() object:self + userInfo:[NSDictionary dictionaryWithObject:fakeEvent forKey:@"NSEvent"]]; +} + +- (id)_bridge +{ + // This method exists to maintain compatibility with Leopard's Dictionary.app, since it + // calls _bridge to get access to convertNSRangeToDOMRange: and convertDOMRangeToNSRange:. + // Return the WebFrame, which implements the compatibility methods. <rdar://problem/6002160> + return [self _frame]; +} + +- (void)_updateMouseoverWithFakeEvent +{ + [self _cancelUpdateMouseoverTimer]; + + NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved + location:[[self window] convertScreenToBase:[NSEvent mouseLocation]] + modifierFlags:[[NSApp currentEvent] modifierFlags] + timestamp:[NSDate timeIntervalSinceReferenceDate] + windowNumber:[[self window] windowNumber] + context:[[NSApp currentEvent] context] + eventNumber:0 clickCount:0 pressure:0]; + + [self _updateMouseoverWithEvent:fakeEvent]; +} + +static void _updateMouseoverTimerCallback(CFRunLoopTimerRef timer, void *info) +{ + WebHTMLView *view = (WebHTMLView *)info; + + [view _updateMouseoverWithFakeEvent]; +} + +- (void)_frameOrBoundsChanged +{ + WebView *webView = [self _webView]; + WebDynamicScrollBarsView *scrollView = [[[webView mainFrame] frameView] _scrollView]; + + NSPoint origin = [[self superview] bounds].origin; + if (!NSEqualPoints(_private->lastScrollPosition, origin) && ![scrollView inProgramaticScroll]) { + if (Frame* coreFrame = core([self _frame])) { + if (FrameView* coreView = coreFrame->view()) { +#ifndef BUILDING_ON_TIGER + _private->inScrollPositionChanged = YES; +#endif + coreView->scrollPositionChangedViaPlatformWidget(); +#ifndef BUILDING_ON_TIGER + _private->inScrollPositionChanged = NO; +#endif + } + } + + [_private->completionController endRevertingChange:NO moveLeft:NO]; + + [[webView _UIDelegateForwarder] webView:webView didScrollDocumentInFrameView:[self _frameView]]; + } + _private->lastScrollPosition = origin; + + if ([self window] && !_private->closed && !_private->updateMouseoverTimer) { + CFRunLoopTimerContext context = { 0, self, NULL, NULL, NULL }; + + // Use a 100ms delay so that the synthetic mouse over update doesn't cause cursor thrashing when pages are loading + // and scrolling rapidly back to back. + _private->updateMouseoverTimer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() + 0.1, 0, 0, 0, + _updateMouseoverTimerCallback, &context); + CFRunLoopAddTimer(CFRunLoopGetCurrent(), _private->updateMouseoverTimer, kCFRunLoopDefaultMode); + } + +#if USE(ACCELERATED_COMPOSITING) && defined(BUILDING_ON_LEOPARD) + [self _updateLayerHostingViewPosition]; +#endif +} + +- (void)_setAsideSubviews +{ + ASSERT(!_private->subviewsSetAside); + ASSERT(_private->savedSubviews == nil); + _private->savedSubviews = _subviews; +#if USE(ACCELERATED_COMPOSITING) + // We need to keep the layer-hosting view in the subviews, otherwise the layers flash. + if (_private->layerHostingView) { + NSArray* newSubviews = [[NSArray alloc] initWithObjects:_private->layerHostingView, nil]; + _subviews = newSubviews; + } else + _subviews = nil; +#else + _subviews = nil; +#endif + _private->subviewsSetAside = YES; + } + + - (void)_restoreSubviews + { + ASSERT(_private->subviewsSetAside); +#if USE(ACCELERATED_COMPOSITING) + if (_private->layerHostingView) { + [_subviews release]; + _subviews = _private->savedSubviews; + } else { + ASSERT(_subviews == nil); + _subviews = _private->savedSubviews; + } +#else + ASSERT(_subviews == nil); + _subviews = _private->savedSubviews; +#endif + _private->savedSubviews = nil; + _private->subviewsSetAside = NO; +} + +#ifndef NDEBUG + +- (void)didAddSubview:(NSView *)subview +{ + if (_private->enumeratingSubviews) + LOG(View, "A view of class %s was added during subview enumeration for layout or printing mode change. This view might paint without first receiving layout.", object_getClassName([subview class])); +} +#endif + +#ifdef BUILDING_ON_TIGER + +// This is called when we are about to draw, but before our dirty rect is propagated to our ancestors. +// That's the perfect time to do a layout, except that ideally we'd want to be sure that we're dirty +// before doing it. As a compromise, when we're opaque we do the layout only when actually asked to +// draw, but when we're transparent we do the layout at this stage so views behind us know that they +// need to be redrawn (in case the layout causes some things to get dirtied). +- (void)_propagateDirtyRectsToOpaqueAncestors +{ + if (![[self _webView] drawsBackground]) + [self _web_updateLayoutAndStyleIfNeededRecursive]; + [super _propagateDirtyRectsToOpaqueAncestors]; +} + +#else + +- (void)viewWillDraw +{ + // On window close we will be called when the datasource is nil, then hit an assert in _topHTMLView + // So check if the dataSource is nil before calling [self _isTopHTMLView], this can be removed + // once the FIXME in _isTopHTMLView is fixed. + if (_private->dataSource && [self _isTopHTMLView]) + [self _web_updateLayoutAndStyleIfNeededRecursive]; + [super viewWillDraw]; +} + +#endif + +// Don't let AppKit even draw subviews. We take care of that. +- (void)_recursiveDisplayRectIfNeededIgnoringOpacity:(NSRect)rect isVisibleRect:(BOOL)isVisibleRect rectIsVisibleRectForView:(NSView *)visibleView topView:(BOOL)topView +{ + // This helps when we print as part of a larger print process. + // If the WebHTMLView itself is what we're printing, then we will never have to do this. + BOOL wasInPrintingMode = _private->printing; + BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen]; + if (isPrinting) { + if (!wasInPrintingMode) + [self _web_setPrintingModeRecursive]; +#ifndef BUILDING_ON_TIGER + else + [self _web_updateLayoutAndStyleIfNeededRecursive]; +#endif + } else if (wasInPrintingMode) + [self _web_clearPrintingModeRecursive]; + +#ifndef BUILDING_ON_TIGER + // There are known cases where -viewWillDraw is not called on all views being drawn. + // See <rdar://problem/6964278> for example. Performing layout at this point prevents us from + // trying to paint without layout (which WebCore now refuses to do, instead bailing out without + // drawing at all), but we may still fail to update any regions dirtied by the layout which are + // not already dirty. + if ([self _needsLayout]) { + NSInteger rectCount; + [self getRectsBeingDrawn:0 count:&rectCount]; + if (rectCount) { + LOG_ERROR("View needs layout. Either -viewWillDraw wasn't called or layout was invalidated during the display operation. Performing layout now."); + [self _web_updateLayoutAndStyleIfNeededRecursive]; + } + } +#else + // Because Tiger does not have viewWillDraw we need to do layout here. + [self _web_updateLayoutAndStyleIfNeededRecursive]; + [_subviews makeObjectsPerformSelector:@selector(_propagateDirtyRectsToOpaqueAncestors)]; +#endif + + [self _setAsideSubviews]; + [super _recursiveDisplayRectIfNeededIgnoringOpacity:rect isVisibleRect:isVisibleRect rectIsVisibleRectForView:visibleView topView:topView]; + [self _restoreSubviews]; + + if (wasInPrintingMode != isPrinting) { + if (wasInPrintingMode) + [self _web_setPrintingModeRecursive]; + else + [self _web_clearPrintingModeRecursive]; + } +} + +// Don't let AppKit even draw subviews. We take care of that. +- (void)_recursiveDisplayAllDirtyWithLockFocus:(BOOL)needsLockFocus visRect:(NSRect)visRect +{ + BOOL needToSetAsideSubviews = !_private->subviewsSetAside; + + BOOL wasInPrintingMode = _private->printing; + BOOL isPrinting = ![NSGraphicsContext currentContextDrawingToScreen]; + + if (needToSetAsideSubviews) { + // This helps when we print as part of a larger print process. + // If the WebHTMLView itself is what we're printing, then we will never have to do this. + if (isPrinting) { + if (!wasInPrintingMode) + [self _web_setPrintingModeRecursive]; +#ifndef BUILDING_ON_TIGER + else + [self _web_updateLayoutAndStyleIfNeededRecursive]; +#endif + } else if (wasInPrintingMode) + [self _web_clearPrintingModeRecursive]; + +#ifdef BUILDING_ON_TIGER + + // Because Tiger does not have viewWillDraw we need to do layout here. + NSRect boundsBeforeLayout = [self bounds]; + if (!NSIsEmptyRect(visRect)) + [self _web_updateLayoutAndStyleIfNeededRecursive]; + + // If layout changes the view's bounds, then we need to recompute the visRect. + // That's because the visRect passed to us was based on the bounds at the time + // we were called. This method is only displayed to draw "all", so it's safe + // to just call visibleRect to compute the entire rectangle. + if (!NSEqualRects(boundsBeforeLayout, [self bounds])) + visRect = [self visibleRect]; + +#endif + + [self _setAsideSubviews]; + } + + [super _recursiveDisplayAllDirtyWithLockFocus:needsLockFocus visRect:visRect]; + + if (needToSetAsideSubviews) { + if (wasInPrintingMode != isPrinting) { + if (wasInPrintingMode) + [self _web_setPrintingModeRecursive]; + else + [self _web_clearPrintingModeRecursive]; + } + + [self _restoreSubviews]; + } +} + +// Don't let AppKit even draw subviews. We take care of that. +- (void)_recursive:(BOOL)recurse displayRectIgnoringOpacity:(NSRect)displayRect inContext:(NSGraphicsContext *)context topView:(BOOL)topView +{ +#ifdef BUILDING_ON_TIGER + // Because Tiger does not have viewWillDraw we need to do layout here. + [self _web_updateLayoutAndStyleIfNeededRecursive]; +#endif + + [self _setAsideSubviews]; + [super _recursive:recurse displayRectIgnoringOpacity:displayRect inContext:context topView:topView]; + [self _restoreSubviews]; +} + +- (BOOL)_insideAnotherHTMLView +{ + return self != [self _topHTMLView]; +} + +- (NSView *)hitTest:(NSPoint)point +{ + // WebHTMLView objects handle all events for objects inside them. + // To get those events, we prevent hit testing from AppKit. + + // But there are three exceptions to this: + // 1) For right mouse clicks and control clicks we don't yet have an implementation + // that works for nested views, so we let the hit testing go through the + // standard NSView code path (needs to be fixed, see bug 4361618). + // 2) Java depends on doing a hit test inside it's mouse moved handling, + // so we let the hit testing go through the standard NSView code path + // when the current event is a mouse move (except when we are calling + // from _updateMouseoverWithEvent, so we have to use a global, + // forceWebHTMLViewHitTest, for that) + // 3) The acceptsFirstMouse: and shouldDelayWindowOrderingForEvent: methods + // both need to figure out which view to check with inside the WebHTMLView. + // They use a global to change the behavior of hitTest: so they can get the + // right view. The global is forceNSViewHitTest and the method they use to + // do the hit testing is _hitViewForEvent:. (But this does not work correctly + // when there is HTML overlapping the view, see bug 4361626) + // 4) NSAccessibilityHitTest relies on this for checking the cursor position. + // Our check for that is whether the event is NSFlagsChanged. This works + // for VoiceOver's Control-Option-F5 command (move focus to item under cursor) + // and Dictionary's Command-Control-D (open dictionary popup for item under cursor). + // This is of course a hack. + + if (_private->closed) + return nil; + + BOOL captureHitsOnSubviews; + if (forceNSViewHitTest) + captureHitsOnSubviews = NO; + else if (forceWebHTMLViewHitTest) + captureHitsOnSubviews = YES; + else { + // FIXME: Why doesn't this include mouse entered/exited events, or other mouse button events? + NSEvent *event = [[self window] currentEvent]; + captureHitsOnSubviews = !([event type] == NSMouseMoved + || [event type] == NSRightMouseDown + || ([event type] == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask) != 0) + || [event type] == NSFlagsChanged); + } + + if (!captureHitsOnSubviews) { + NSView* hitView = [super hitTest:point]; +#if USE(ACCELERATED_COMPOSITING) + if (_private && hitView == _private->layerHostingView) + hitView = self; +#endif + return hitView; + } + if ([[self superview] mouse:point inRect:[self frame]]) + return self; + return nil; +} + +- (void)_clearLastHitViewIfSelf +{ + if (lastHitView == self) + lastHitView = nil; +} + +- (NSTrackingRectTag)addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside +{ + ASSERT(_private->trackingRectOwner == nil); + _private->trackingRectOwner = owner; + _private->trackingRectUserData = data; + return TRACKING_RECT_TAG; +} + +- (NSTrackingRectTag)_addTrackingRect:(NSRect)rect owner:(id)owner userData:(void *)data assumeInside:(BOOL)assumeInside useTrackingNum:(int)tag +{ + ASSERT(tag == 0 || tag == TRACKING_RECT_TAG); + ASSERT(_private->trackingRectOwner == nil); + _private->trackingRectOwner = owner; + _private->trackingRectUserData = data; + return TRACKING_RECT_TAG; +} + +- (void)_addTrackingRects:(NSRect *)rects owner:(id)owner userDataList:(void **)userDataList assumeInsideList:(BOOL *)assumeInsideList trackingNums:(NSTrackingRectTag *)trackingNums count:(int)count +{ + ASSERT(count == 1); + ASSERT(trackingNums[0] == 0 || trackingNums[0] == TRACKING_RECT_TAG); + ASSERT(_private->trackingRectOwner == nil); + _private->trackingRectOwner = owner; + _private->trackingRectUserData = userDataList[0]; + trackingNums[0] = TRACKING_RECT_TAG; +} + +- (void)removeTrackingRect:(NSTrackingRectTag)tag +{ + if (tag == 0) + return; + + if (_private && (tag == TRACKING_RECT_TAG)) { + _private->trackingRectOwner = nil; + return; + } + + if (_private && (tag == _private->lastToolTipTag)) { + [super removeTrackingRect:tag]; + _private->lastToolTipTag = 0; + return; + } + + // If any other tracking rect is being removed, we don't know how it was created + // and it's possible there's a leak involved (see 3500217) + ASSERT_NOT_REACHED(); +} + +- (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count +{ + int i; + for (i = 0; i < count; ++i) { + int tag = tags[i]; + if (tag == 0) + continue; + ASSERT(tag == TRACKING_RECT_TAG); + if (_private != nil) { + _private->trackingRectOwner = nil; + } + } +} + +- (void)_sendToolTipMouseExited +{ + // Nothing matters except window, trackingNumber, and userData. + NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited + location:NSMakePoint(0, 0) + modifierFlags:0 + timestamp:0 + windowNumber:[[self window] windowNumber] + context:NULL + eventNumber:0 + trackingNumber:TRACKING_RECT_TAG + userData:_private->trackingRectUserData]; + [_private->trackingRectOwner mouseExited:fakeEvent]; +} + +- (void)_sendToolTipMouseEntered +{ + // Nothing matters except window, trackingNumber, and userData. + NSEvent *fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered + location:NSMakePoint(0, 0) + modifierFlags:0 + timestamp:0 + windowNumber:[[self window] windowNumber] + context:NULL + eventNumber:0 + trackingNumber:TRACKING_RECT_TAG + userData:_private->trackingRectUserData]; + [_private->trackingRectOwner mouseEntered:fakeEvent]; +} + +- (void)_setToolTip:(NSString *)string +{ + NSString *toolTip = [string length] == 0 ? nil : string; + NSString *oldToolTip = _private->toolTip; + if ((toolTip == nil || oldToolTip == nil) ? toolTip == oldToolTip : [toolTip isEqualToString:oldToolTip]) { + return; + } + if (oldToolTip) { + [self _sendToolTipMouseExited]; + [oldToolTip release]; + } + _private->toolTip = [toolTip copy]; + if (toolTip) { + // See radar 3500217 for why we remove all tooltips rather than just the single one we created. + [self removeAllToolTips]; + NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000); + _private->lastToolTipTag = [self addToolTipRect:wideOpenRect owner:self userData:NULL]; + [self _sendToolTipMouseEntered]; + } +} + +- (NSString *)view:(NSView *)view stringForToolTip:(NSToolTipTag)tag point:(NSPoint)point userData:(void *)data +{ + return [[_private->toolTip copy] autorelease]; +} + +- (void)_updateMouseoverWithEvent:(NSEvent *)event +{ + if (_private->closed) + return; + + NSView *contentView = [[event window] contentView]; + NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil]; + + forceWebHTMLViewHitTest = YES; + NSView *hitView = [contentView hitTest:locationForHitTest]; + forceWebHTMLViewHitTest = NO; + + WebHTMLView *view = nil; + if ([hitView isKindOfClass:[WebHTMLView class]] && ![[(WebHTMLView *)hitView _webView] isHoverFeedbackSuspended]) + view = (WebHTMLView *)hitView; + + if (view) + [view retain]; + + if (lastHitView != view && lastHitView && [lastHitView _frame]) { + // If we are moving out of a view (or frame), let's pretend the mouse moved + // all the way out of that view. But we have to account for scrolling, because + // WebCore doesn't understand our clipping. + NSRect visibleRect = [[[[lastHitView _frame] frameView] _scrollView] documentVisibleRect]; + float yScroll = visibleRect.origin.y; + float xScroll = visibleRect.origin.x; + + NSEvent *event = [NSEvent mouseEventWithType:NSMouseMoved + location:NSMakePoint(-1 - xScroll, -1 - yScroll) + modifierFlags:[[NSApp currentEvent] modifierFlags] + timestamp:[NSDate timeIntervalSinceReferenceDate] + windowNumber:[[view window] windowNumber] + context:[[NSApp currentEvent] context] + eventNumber:0 clickCount:0 pressure:0]; + if (Frame* lastHitCoreFrame = core([lastHitView _frame])) + lastHitCoreFrame->eventHandler()->mouseMoved(event); + } + + lastHitView = view; + + if (view) { + if (Frame* coreFrame = core([view _frame])) + coreFrame->eventHandler()->mouseMoved(event); + + [view release]; + } +} + ++ (NSArray *)_insertablePasteboardTypes +{ + static NSArray *types = nil; + if (!types) { + types = [[NSArray alloc] initWithObjects:WebArchivePboardType, NSHTMLPboardType, NSFilenamesPboardType, NSTIFFPboardType, NSPDFPboardType, +#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) + NSPICTPboardType, +#endif + NSURLPboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, NSColorPboardType, kUTTypePNG, nil]; + CFRetain(types); + } + return types; +} + ++ (NSArray *)_selectionPasteboardTypes +{ + // FIXME: We should put data for NSHTMLPboardType on the pasteboard but Microsoft Excel doesn't like our format of HTML (3640423). + return [NSArray arrayWithObjects:WebArchivePboardType, NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil]; +} + +- (void)pasteboardChangedOwner:(NSPasteboard *)pasteboard +{ + [self setPromisedDragTIFFDataSource:0]; +} + +- (void)pasteboard:(NSPasteboard *)pasteboard provideDataForType:(NSString *)type +{ + if ([type isEqual:NSRTFDPboardType] && [[pasteboard types] containsObject:WebArchivePboardType]) { + WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]]; + [pasteboard _web_writePromisedRTFDFromArchive:archive containsImage:[[pasteboard types] containsObject:NSTIFFPboardType]]; + [archive release]; + } else if ([type isEqual:NSTIFFPboardType] && [self promisedDragTIFFDataSource]) { + if (Image* image = [self promisedDragTIFFDataSource]->image()) + [pasteboard setData:(NSData *)image->getTIFFRepresentation() forType:NSTIFFPboardType]; + [self setPromisedDragTIFFDataSource:0]; + } +} + +- (void)_handleAutoscrollForMouseDragged:(NSEvent *)event +{ + [self autoscroll:event]; + [self _startAutoscrollTimer:event]; +} + +- (WebPluginController *)_pluginController +{ + return _private->pluginController; +} + +- (void)_layoutForPrinting +{ + // Set printing mode temporarily so we can adjust the size of the view. This will allow + // AppKit's pagination code to use the correct height for the page content. Leaving printing + // mode on indefinitely would interfere with Mail's printing mechanism (at least), so we just + // turn it off again after adjusting the size. + [self _web_setPrintingModeRecursiveAndAdjustViewSize]; + [self _web_clearPrintingModeRecursive]; +} + +- (void)_smartInsertForString:(NSString *)pasteString replacingRange:(DOMRange *)rangeToReplace beforeString:(NSString **)beforeString afterString:(NSString **)afterString +{ + if (!pasteString || !rangeToReplace || ![[self _webView] smartInsertDeleteEnabled]) { + if (beforeString) + *beforeString = nil; + if (afterString) + *afterString = nil; + return; + } + + [[self _frame] _smartInsertForString:pasteString replacingRange:rangeToReplace beforeString:beforeString afterString:afterString]; +} + +- (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard +{ + return [[self _webView] smartInsertDeleteEnabled] && [[pasteboard types] containsObject:WebSmartPastePboardType]; +} + +- (void)_startAutoscrollTimer:(NSEvent *)triggerEvent +{ + if (_private->autoscrollTimer == nil) { + _private->autoscrollTimer = [[NSTimer scheduledTimerWithTimeInterval:AUTOSCROLL_INTERVAL + target:self selector:@selector(_autoscroll) userInfo:nil repeats:YES] retain]; + _private->autoscrollTriggerEvent = [triggerEvent retain]; + } +} + +// FIXME: _selectionRect is deprecated in favor of selectionRect, which is in protocol WebDocumentSelection. +// We can't remove this yet because it's still in use by Mail. +- (NSRect)_selectionRect +{ + return [self selectionRect]; +} + +- (void)_stopAutoscrollTimer +{ + NSTimer *timer = _private->autoscrollTimer; + _private->autoscrollTimer = nil; + [_private->autoscrollTriggerEvent release]; + _private->autoscrollTriggerEvent = nil; + [timer invalidate]; + [timer release]; +} + +- (void)_autoscroll +{ + // Guarantee that the autoscroll timer is invalidated, even if we don't receive + // a mouse up event. + BOOL isStillDown = CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState, kCGMouseButtonLeft); + if (!isStillDown){ + [self _stopAutoscrollTimer]; + return; + } + + NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseDragged + location:[[self window] convertScreenToBase:[NSEvent mouseLocation]] + modifierFlags:[[NSApp currentEvent] modifierFlags] + timestamp:[NSDate timeIntervalSinceReferenceDate] + windowNumber:[[self window] windowNumber] + context:[[NSApp currentEvent] context] + eventNumber:0 clickCount:0 pressure:0]; + [self mouseDragged:fakeEvent]; +} + +- (BOOL)_canEdit +{ + Frame* coreFrame = core([self _frame]); + return coreFrame && coreFrame->editor()->canEdit(); +} + +- (BOOL)_canEditRichly +{ + Frame* coreFrame = core([self _frame]); + return coreFrame && coreFrame->editor()->canEditRichly(); +} + +- (BOOL)_canAlterCurrentSelection +{ + return [self _hasSelectionOrInsertionPoint] && [self _isEditable]; +} + +- (BOOL)_hasSelection +{ + Frame* coreFrame = core([self _frame]); + return coreFrame && coreFrame->selection()->isRange(); +} + +- (BOOL)_hasSelectionOrInsertionPoint +{ + Frame* coreFrame = core([self _frame]); + return coreFrame && coreFrame->selection()->isCaretOrRange(); +} + +- (BOOL)_hasInsertionPoint +{ + Frame* coreFrame = core([self _frame]); + return coreFrame && coreFrame->selection()->isCaret(); +} + +- (BOOL)_isEditable +{ + Frame* coreFrame = core([self _frame]); + return coreFrame && coreFrame->selection()->isContentEditable(); +} + +- (BOOL)_transparentBackground +{ + return _private->transparentBackground; +} + +- (void)_setTransparentBackground:(BOOL)f +{ + _private->transparentBackground = f; +} + +- (NSImage *)_selectionDraggingImage +{ + if (![self _hasSelection]) + return nil; + NSImage *dragImage = core([self _frame])->selectionImage(); + [dragImage _web_dissolveToFraction:WebDragImageAlpha]; + return dragImage; +} + +- (NSRect)_selectionDraggingRect +{ + // Mail currently calls this method. We can eliminate it when Mail no longer calls it. + return [self selectionRect]; +} + +- (DOMNode *)_insertOrderedList +{ + Frame* coreFrame = core([self _frame]); + return coreFrame ? kit(coreFrame->editor()->insertOrderedList().get()) : nil; +} + +- (DOMNode *)_insertUnorderedList +{ + Frame* coreFrame = core([self _frame]); + return coreFrame ? kit(coreFrame->editor()->insertUnorderedList().get()) : nil; +} + +- (BOOL)_canIncreaseSelectionListLevel +{ + Frame* coreFrame = core([self _frame]); + return coreFrame && coreFrame->editor()->canIncreaseSelectionListLevel(); +} + +- (BOOL)_canDecreaseSelectionListLevel +{ + Frame* coreFrame = core([self _frame]); + return coreFrame && coreFrame->editor()->canDecreaseSelectionListLevel(); +} + +- (DOMNode *)_increaseSelectionListLevel +{ + Frame* coreFrame = core([self _frame]); + return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevel().get()) : nil; +} + +- (DOMNode *)_increaseSelectionListLevelOrdered +{ + Frame* coreFrame = core([self _frame]); + return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelOrdered().get()) : nil; +} + +- (DOMNode *)_increaseSelectionListLevelUnordered +{ + Frame* coreFrame = core([self _frame]); + return coreFrame ? kit(coreFrame->editor()->increaseSelectionListLevelUnordered().get()) : nil; +} + +- (void)_decreaseSelectionListLevel +{ + Frame* coreFrame = core([self _frame]); + if (coreFrame) + coreFrame->editor()->decreaseSelectionListLevel(); +} + +- (void)_setHighlighter:(id<WebHTMLHighlighter>)highlighter ofType:(NSString*)type +{ + if (!_private->highlighters) + _private->highlighters = [[NSMutableDictionary alloc] init]; + [_private->highlighters setObject:highlighter forKey:type]; +} + +- (void)_removeHighlighterOfType:(NSString*)type +{ + [_private->highlighters removeObjectForKey:type]; +} + +- (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard +{ + ASSERT([self _hasSelection]); + NSArray *types = [self pasteboardTypesForSelection]; + + // Don't write RTFD to the pasteboard when the copied attributed string has no attachments. + NSAttributedString *attributedString = [self selectedAttributedString]; + NSMutableArray *mutableTypes = nil; + if (![attributedString containsAttachments]) { + mutableTypes = [types mutableCopy]; + [mutableTypes removeObject:NSRTFDPboardType]; + types = mutableTypes; + } + + [pasteboard declareTypes:types owner:[self _topHTMLView]]; + [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:attributedString]; + [mutableTypes release]; +} + +- (void)close +{ + // Check for a nil _private here in case we were created with initWithCoder. In that case, the WebView is just throwing + // out the archived WebHTMLView and recreating a new one if needed. So close doesn't need to do anything in that case. + if (!_private || _private->closed) + return; + + _private->closed = YES; + + [self _cancelUpdateMouseoverTimer]; + [self _clearLastHitViewIfSelf]; + [self _removeMouseMovedObserverUnconditionally]; + [self _removeWindowObservers]; + [self _removeSuperviewObservers]; + [_private->pluginController destroyAllPlugins]; + [_private->pluginController setDataSource:nil]; + // remove tooltips before clearing _private so removeTrackingRect: will work correctly + [self removeAllToolTips]; + + [_private clear]; + + Page* page = core([self _webView]); + if (page) + page->dragController()->setDraggingImageURL(KURL()); +} + +- (BOOL)_hasHTMLDocument +{ + Frame* coreFrame = core([self _frame]); + if (!coreFrame) + return NO; + Document* document = coreFrame->document(); + return document && document->isHTMLDocument(); +} + +- (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard + forType:(NSString *)pboardType + inContext:(DOMRange *)context + subresources:(NSArray **)subresources +{ + if (pboardType == WebArchivePboardType) { + WebArchive *archive = [[WebArchive alloc] initWithData:[pasteboard dataForType:WebArchivePboardType]]; + if (subresources) + *subresources = [archive subresources]; + DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithArchive:archive]; + [archive release]; + return fragment; + } + if (pboardType == NSFilenamesPboardType) + return [self _documentFragmentWithPaths:[pasteboard propertyListForType:NSFilenamesPboardType]]; + + if (pboardType == NSHTMLPboardType) { + NSString *HTMLString = [pasteboard stringForType:NSHTMLPboardType]; + // This is a hack to make Microsoft's HTML pasteboard data work. See 3778785. + if ([HTMLString hasPrefix:@"Version:"]) { + NSRange range = [HTMLString rangeOfString:@"<html" options:NSCaseInsensitiveSearch]; + if (range.location != NSNotFound) + HTMLString = [HTMLString substringFromIndex:range.location]; + } + if ([HTMLString length] == 0) + return nil; + + return [[self _frame] _documentFragmentWithMarkupString:HTMLString baseURLString:nil]; + } + + // The _hasHTMLDocument clause here is a workaround for a bug in NSAttributedString: Radar 5052369. + // If we call _documentFromRange on an XML document we'll get "setInnerHTML: method not found". + // FIXME: Remove this once bug 5052369 is fixed. + if ([self _hasHTMLDocument] && (pboardType == NSRTFPboardType || pboardType == NSRTFDPboardType)) { + NSAttributedString *string = nil; + if (pboardType == NSRTFDPboardType) + string = [[NSAttributedString alloc] initWithRTFD:[pasteboard dataForType:NSRTFDPboardType] documentAttributes:NULL]; + if (string == nil) + string = [[NSAttributedString alloc] initWithRTF:[pasteboard dataForType:NSRTFPboardType] documentAttributes:NULL]; + if (string == nil) + return nil; + + NSDictionary *documentAttributes = [[NSDictionary alloc] initWithObjectsAndKeys: + [[self class] _excludedElementsForAttributedStringConversion], NSExcludedElementsDocumentAttribute, + self, @"WebResourceHandler", nil]; + NSArray *s; + + BOOL wasDeferringCallbacks = [[self _webView] defersCallbacks]; + if (!wasDeferringCallbacks) + [[self _webView] setDefersCallbacks:YES]; + + DOMDocumentFragment *fragment = [string _documentFromRange:NSMakeRange(0, [string length]) + document:[[self _frame] DOMDocument] + documentAttributes:documentAttributes + subresources:&s]; + if (subresources) + *subresources = s; + + NSEnumerator *e = [s objectEnumerator]; + WebResource *r; + while ((r = [e nextObject])) + [[self _dataSource] addSubresource:r]; + + if (!wasDeferringCallbacks) + [[self _webView] setDefersCallbacks:NO]; + + [documentAttributes release]; + [string release]; + return fragment; + } + if (pboardType == NSTIFFPboardType) { + WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSTIFFPboardType] + URL:uniqueURLWithRelativePart(@"image.tiff") + MIMEType:@"image/tiff" + textEncodingName:nil + frameName:nil]; + DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource]; + [resource release]; + return fragment; + } + if (pboardType == NSPDFPboardType) { + WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPDFPboardType] + URL:uniqueURLWithRelativePart(@"application.pdf") + MIMEType:@"application/pdf" + textEncodingName:nil + frameName:nil]; + DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource]; + [resource release]; + return fragment; + } +#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) + if (pboardType == NSPICTPboardType) { + WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:NSPICTPboardType] + URL:uniqueURLWithRelativePart(@"image.pict") + MIMEType:@"image/pict" + textEncodingName:nil + frameName:nil]; + DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource]; + [resource release]; + return fragment; + } +#endif + // Only 10.5 and higher support setting and retrieving pasteboard types with UTIs, but we don't believe + // that any applications on Tiger put types for which we only have a UTI, like PNG, on the pasteboard. + if ([pboardType isEqualToString:(NSString*)kUTTypePNG]) { + WebResource *resource = [[WebResource alloc] initWithData:[pasteboard dataForType:(NSString*)kUTTypePNG] + URL:uniqueURLWithRelativePart(@"image.png") + MIMEType:@"image/png" + textEncodingName:nil + frameName:nil]; + DOMDocumentFragment *fragment = [[self _dataSource] _documentFragmentWithImageResource:resource]; + [resource release]; + return fragment; + } + if (pboardType == NSURLPboardType) { + NSURL *URL = [NSURL URLFromPasteboard:pasteboard]; + DOMDocument* document = [[self _frame] DOMDocument]; + ASSERT(document); + if (!document) + return nil; + DOMHTMLAnchorElement *anchor = (DOMHTMLAnchorElement *)[document createElement:@"a"]; + NSString *URLString = [URL _web_originalDataAsString]; // Original data is ASCII-only, so there is no need to precompose. + if ([URLString length] == 0) + return nil; + NSString *URLTitleString = [[pasteboard stringForType:WebURLNamePboardType] precomposedStringWithCanonicalMapping]; + DOMText *text = [document createTextNode:URLTitleString]; + [anchor setHref:URLString]; + [anchor appendChild:text]; + DOMDocumentFragment *fragment = [document createDocumentFragment]; + [fragment appendChild:anchor]; + return fragment; + } + if (pboardType == NSStringPboardType) + return kit(createFragmentFromText(core(context), [[pasteboard stringForType:NSStringPboardType] precomposedStringWithCanonicalMapping]).get()); + return nil; +} + +#if ENABLE(NETSCAPE_PLUGIN_API) +- (void)_pauseNullEventsForAllNetscapePlugins +{ + NSArray *subviews = [self subviews]; + unsigned int subviewCount = [subviews count]; + unsigned int subviewIndex; + + for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) { + NSView *subview = [subviews objectAtIndex:subviewIndex]; + if ([subview isKindOfClass:[WebBaseNetscapePluginView class]]) + [(WebBaseNetscapePluginView *)subview stopTimers]; + } +} +#endif + +#if ENABLE(NETSCAPE_PLUGIN_API) +- (void)_resumeNullEventsForAllNetscapePlugins +{ + NSArray *subviews = [self subviews]; + unsigned int subviewCount = [subviews count]; + unsigned int subviewIndex; + + for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) { + NSView *subview = [subviews objectAtIndex:subviewIndex]; + if ([subview isKindOfClass:[WebBaseNetscapePluginView class]]) + [(WebBaseNetscapePluginView *)subview restartTimers]; + } +} +#endif + +- (BOOL)_isUsingAcceleratedCompositing +{ +#if USE(ACCELERATED_COMPOSITING) + return _private->layerHostingView != nil; +#else + return NO; +#endif +} + +- (NSView *)_compositingLayersHostingView +{ +#if USE(ACCELERATED_COMPOSITING) + return _private->layerHostingView; +#else + return 0; +#endif +} + +- (BOOL)_isInPrintMode +{ + return _private->printing; +} + +- (BOOL)_beginPrintModeWithMinimumPageWidth:(CGFloat)minimumPageWidth height:(CGFloat)minimumPageHeight maximumPageWidth:(CGFloat)maximumPageWidth +{ + Frame* frame = core([self _frame]); + if (!frame) + return NO; + + if (frame->document() && frame->document()->isFrameSet()) { + minimumPageWidth = 0; + minimumPageHeight = 0; + maximumPageWidth = 0; + } + + [self _setPrinting:YES minimumPageWidth:minimumPageWidth height:minimumPageHeight maximumPageWidth:maximumPageWidth adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]]; + return YES; +} + +- (BOOL)_beginPrintModeWithPageWidth:(float)pageWidth height:(float)pageHeight shrinkToFit:(BOOL)shrinkToFit +{ + Frame* frame = core([self _frame]); + if (!frame) + return NO; + + float minLayoutWidth = 0; + float minLayoutHeight = 0; + float maxLayoutWidth = 0; + + // If we are a frameset just print with the layout we have onscreen, otherwise relayout + // according to the page width. + if (!frame->document() || !frame->document()->isFrameSet()) { + minLayoutWidth = shrinkToFit ? pageWidth * _WebHTMLViewPrintingMinimumShrinkFactor : pageWidth; + minLayoutHeight = shrinkToFit ? pageHeight * _WebHTMLViewPrintingMinimumShrinkFactor : pageHeight; + maxLayoutWidth = shrinkToFit ? pageWidth * _WebHTMLViewPrintingMaximumShrinkFactor : pageWidth; + } + [self _setPrinting:YES minimumPageWidth:minLayoutWidth height:minLayoutHeight maximumPageWidth:maxLayoutWidth adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]]; + + return YES; +} + +- (void)_endPrintMode +{ + [self _setPrinting:NO minimumPageWidth:0 height:0 maximumPageWidth:0 adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]]; +} + +- (BOOL)_isInScreenPaginationMode +{ + return _private->paginateScreenContent; +} + +- (BOOL)_beginScreenPaginationModeWithPageSize:(CGSize)pageSize shrinkToFit:(BOOL)shrinkToFit +{ + Frame* frame = core([self _frame]); + if (!frame) + return NO; + + CGFloat minLayoutWidth = 0; + CGFloat minLayoutHeight = 0; + CGFloat maxLayoutWidth = 0; + + // If we are a frameset just print with the layout we have on the screen. Otherwise do a relayout + // according to the page width. + if (!frame->document() || !frame->document()->isFrameSet()) { + minLayoutWidth = shrinkToFit ? pageSize.width * _WebHTMLViewPrintingMinimumShrinkFactor : pageSize.width; + minLayoutHeight = shrinkToFit ? pageSize.height * _WebHTMLViewPrintingMinimumShrinkFactor : pageSize.height; + maxLayoutWidth = shrinkToFit ? pageSize.width * _WebHTMLViewPrintingMaximumShrinkFactor : pageSize.width; + } + [self _setPrinting:[self _isInPrintMode] minimumPageWidth:minLayoutWidth height:minLayoutHeight maximumPageWidth:maxLayoutWidth adjustViewSize:YES paginateScreenContent:YES]; + + return YES; +} + +- (void)_endScreenPaginationMode +{ + [self _setPrinting:[self _isInPrintMode] minimumPageWidth:0 height:0 maximumPageWidth:0 adjustViewSize:YES paginateScreenContent:NO]; +} + +- (CGFloat)_adjustedBottomOfPageWithTop:(CGFloat)top bottom:(CGFloat)bottom limit:(CGFloat)bottomLimit +{ + Frame* frame = core([self _frame]); + if (!frame) + return bottom; + + FrameView* view = frame->view(); + if (!view) + return bottom; + + float newBottom; + view->adjustPageHeightDeprecated(&newBottom, top, bottom, bottomLimit); + +#ifdef __LP64__ + // If the new bottom is equal to the old bottom (when both are treated as floats), we just return the original + // bottom. This prevents rounding errors that can occur when converting newBottom to a double. + if (fabs(static_cast<float>(bottom) - newBottom) <= numeric_limits<float>::epsilon()) + return bottom; + else +#endif + return newBottom; +} + +@end + +@implementation NSView (WebHTMLViewFileInternal) + +- (void)_web_addDescendantWebHTMLViewsToArray:(NSMutableArray *)array +{ + unsigned count = [_subviews count]; + for (unsigned i = 0; i < count; ++i) { + NSView *child = [_subviews objectAtIndex:i]; + if ([child isKindOfClass:[WebHTMLView class]]) + [array addObject:child]; + [child _web_addDescendantWebHTMLViewsToArray:array]; + } +} + +@end + +@implementation NSMutableDictionary (WebHTMLViewFileInternal) + +- (void)_web_setObjectIfNotNil:(id)object forKey:(id)key +{ + if (object == nil) { + [self removeObjectForKey:key]; + } else { + [self setObject:object forKey:key]; + } +} + +@end + +static bool matchesExtensionOrEquivalent(NSString *filename, NSString *extension) +{ + NSString *extensionAsSuffix = [@"." stringByAppendingString:extension]; + return [filename _webkit_hasCaseInsensitiveSuffix:extensionAsSuffix] + || ([extension _webkit_isCaseInsensitiveEqualToString:@"jpeg"] + && [filename _webkit_hasCaseInsensitiveSuffix:@".jpg"]); +} + +#ifdef BUILDING_ON_TIGER + +// The following is a workaround for +// <rdar://problem/3429631> window stops getting mouse moved events after first tooltip appears +// The trick is to define a category on NSToolTipPanel that implements setAcceptsMouseMovedEvents:. +// Since the category will be searched before the real class, we'll prevent the flag from being +// set on the tool tip panel. + +@interface NSToolTipPanel : NSPanel +@end + +@interface NSToolTipPanel (WebHTMLViewFileInternal) +@end + +@implementation NSToolTipPanel (WebHTMLViewFileInternal) + +- (void)setAcceptsMouseMovedEvents:(BOOL)flag +{ + // Do nothing, preventing the tool tip panel from trying to accept mouse-moved events. +} + +@end + +#endif + +@implementation WebHTMLView + ++ (void)initialize +{ + [NSApp registerServicesMenuSendTypes:[[self class] _selectionPasteboardTypes] + returnTypes:[[self class] _insertablePasteboardTypes]]; + JSC::initializeThreading(); + WTF::initializeMainThreadToProcessMainThread(); +#ifndef BUILDING_ON_TIGER + WebCoreObjCFinalizeOnMainThread(self); +#endif +} + +- (id)initWithFrame:(NSRect)frame +{ + self = [super initWithFrame:frame]; + if (!self) + return nil; + + [self setFocusRingType:NSFocusRingTypeNone]; + + // Make all drawing go through us instead of subviews. + [self _setDrawsOwnDescendants:YES]; + + _private = [[WebHTMLViewPrivate alloc] init]; + + _private->pluginController = [[WebPluginController alloc] initWithDocumentView:self]; + + return self; +} + +- (void)dealloc +{ + if (WebCoreObjCScheduleDeallocateOnMainThread([WebHTMLView class], self)) + return; + + // We can't assert that close has already been called because + // this view can be removed from it's superview, even though + // it could be needed later, so close if needed. + [self close]; + [_private release]; + _private = nil; + [super dealloc]; +} + +- (void)finalize +{ + ASSERT_MAIN_THREAD(); + // We can't assert that close has already been called because + // this view can be removed from it's superview, even though + // it could be needed later, so close if needed. + [self close]; + [super finalize]; +} + +// Returns YES if the delegate returns YES (so we should do no more work). +- (BOOL)callDelegateDoCommandBySelectorIfNeeded:(SEL)selector +{ + BOOL callerAlreadyCalledDelegate = _private->selectorForDoCommandBySelector == selector; + _private->selectorForDoCommandBySelector = 0; + if (callerAlreadyCalledDelegate) + return NO; + WebView *webView = [self _webView]; + return [[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector]; +} + +typedef HashMap<SEL, String> SelectorNameMap; + +// Map selectors into Editor command names. +// This is not needed for any selectors that have the same name as the Editor command. +static const SelectorNameMap* createSelectorExceptionMap() +{ + SelectorNameMap* map = new HashMap<SEL, String>; + + map->add(@selector(insertNewlineIgnoringFieldEditor:), "InsertNewline"); + map->add(@selector(insertParagraphSeparator:), "InsertNewline"); + map->add(@selector(insertTabIgnoringFieldEditor:), "InsertTab"); + map->add(@selector(pageDown:), "MovePageDown"); + map->add(@selector(pageDownAndModifySelection:), "MovePageDownAndModifySelection"); + map->add(@selector(pageUp:), "MovePageUp"); + map->add(@selector(pageUpAndModifySelection:), "MovePageUpAndModifySelection"); + + return map; +} + +static String commandNameForSelector(SEL selector) +{ + // Check the exception map first. + static const SelectorNameMap* exceptionMap = createSelectorExceptionMap(); + SelectorNameMap::const_iterator it = exceptionMap->find(selector); + if (it != exceptionMap->end()) + return it->second; + + // Remove the trailing colon. + // No need to capitalize the command name since Editor command names are + // not case sensitive. + const char* selectorName = sel_getName(selector); + size_t selectorNameLength = strlen(selectorName); + if (selectorNameLength < 2 || selectorName[selectorNameLength - 1] != ':') + return String(); + return String(selectorName, selectorNameLength - 1); +} + +- (Editor::Command)coreCommandBySelector:(SEL)selector +{ + Frame* coreFrame = core([self _frame]); + if (!coreFrame) + return Editor::Command(); + return coreFrame->editor()->command(commandNameForSelector(selector)); +} + +- (Editor::Command)coreCommandByName:(const char*)name +{ + Frame* coreFrame = core([self _frame]); + if (!coreFrame) + return Editor::Command(); + return coreFrame->editor()->command(name); +} + +- (void)executeCoreCommandBySelector:(SEL)selector +{ + if ([self callDelegateDoCommandBySelectorIfNeeded:selector]) + return; + [self coreCommandBySelector:selector].execute(); +} + +- (void)executeCoreCommandByName:(const char*)name +{ + [self coreCommandByName:name].execute(); +} + +// These commands are forwarded to the Editor object in WebCore. +// Ideally we'd do this for all editing commands; more of the code +// should be moved from here to there, and more commands should be +// added to this list. + +// FIXME: Maybe we should set things up so that all these share a single method implementation function. +// The functions are identical. + +#define WEBCORE_COMMAND(command) - (void)command:(id)sender { [self executeCoreCommandBySelector:_cmd]; } + +WEBCORE_COMMAND(alignCenter) +WEBCORE_COMMAND(alignJustified) +WEBCORE_COMMAND(alignLeft) +WEBCORE_COMMAND(alignRight) +WEBCORE_COMMAND(copy) +WEBCORE_COMMAND(cut) +WEBCORE_COMMAND(paste) +WEBCORE_COMMAND(delete) +WEBCORE_COMMAND(deleteBackward) +WEBCORE_COMMAND(deleteBackwardByDecomposingPreviousCharacter) +WEBCORE_COMMAND(deleteForward) +WEBCORE_COMMAND(deleteToBeginningOfLine) +WEBCORE_COMMAND(deleteToBeginningOfParagraph) +WEBCORE_COMMAND(deleteToEndOfLine) +WEBCORE_COMMAND(deleteToEndOfParagraph) +WEBCORE_COMMAND(deleteToMark) +WEBCORE_COMMAND(deleteWordBackward) +WEBCORE_COMMAND(deleteWordForward) +WEBCORE_COMMAND(ignoreSpelling) +WEBCORE_COMMAND(indent) +WEBCORE_COMMAND(insertBacktab) +WEBCORE_COMMAND(insertLineBreak) +WEBCORE_COMMAND(insertNewline) +WEBCORE_COMMAND(insertNewlineIgnoringFieldEditor) +WEBCORE_COMMAND(insertParagraphSeparator) +WEBCORE_COMMAND(insertTab) +WEBCORE_COMMAND(insertTabIgnoringFieldEditor) +WEBCORE_COMMAND(makeTextWritingDirectionLeftToRight) +WEBCORE_COMMAND(makeTextWritingDirectionNatural) +WEBCORE_COMMAND(makeTextWritingDirectionRightToLeft) +WEBCORE_COMMAND(moveBackward) +WEBCORE_COMMAND(moveBackwardAndModifySelection) +WEBCORE_COMMAND(moveDown) +WEBCORE_COMMAND(moveDownAndModifySelection) +WEBCORE_COMMAND(moveForward) +WEBCORE_COMMAND(moveForwardAndModifySelection) +WEBCORE_COMMAND(moveLeft) +WEBCORE_COMMAND(moveLeftAndModifySelection) +WEBCORE_COMMAND(moveParagraphBackwardAndModifySelection) +WEBCORE_COMMAND(moveParagraphForwardAndModifySelection) +WEBCORE_COMMAND(moveRight) +WEBCORE_COMMAND(moveRightAndModifySelection) +WEBCORE_COMMAND(moveToBeginningOfDocument) +WEBCORE_COMMAND(moveToBeginningOfDocumentAndModifySelection) +WEBCORE_COMMAND(moveToBeginningOfLine) +WEBCORE_COMMAND(moveToBeginningOfLineAndModifySelection) +WEBCORE_COMMAND(moveToBeginningOfParagraph) +WEBCORE_COMMAND(moveToBeginningOfParagraphAndModifySelection) +WEBCORE_COMMAND(moveToBeginningOfSentence) +WEBCORE_COMMAND(moveToBeginningOfSentenceAndModifySelection) +WEBCORE_COMMAND(moveToEndOfDocument) +WEBCORE_COMMAND(moveToEndOfDocumentAndModifySelection) +WEBCORE_COMMAND(moveToEndOfLine) +WEBCORE_COMMAND(moveToEndOfLineAndModifySelection) +WEBCORE_COMMAND(moveToEndOfParagraph) +WEBCORE_COMMAND(moveToEndOfParagraphAndModifySelection) +WEBCORE_COMMAND(moveToEndOfSentence) +WEBCORE_COMMAND(moveToEndOfSentenceAndModifySelection) +WEBCORE_COMMAND(moveToLeftEndOfLine) +WEBCORE_COMMAND(moveToLeftEndOfLineAndModifySelection) +WEBCORE_COMMAND(moveToRightEndOfLine) +WEBCORE_COMMAND(moveToRightEndOfLineAndModifySelection) +WEBCORE_COMMAND(moveUp) +WEBCORE_COMMAND(moveUpAndModifySelection) +WEBCORE_COMMAND(moveWordBackward) +WEBCORE_COMMAND(moveWordBackwardAndModifySelection) +WEBCORE_COMMAND(moveWordForward) +WEBCORE_COMMAND(moveWordForwardAndModifySelection) +WEBCORE_COMMAND(moveWordLeft) +WEBCORE_COMMAND(moveWordLeftAndModifySelection) +WEBCORE_COMMAND(moveWordRight) +WEBCORE_COMMAND(moveWordRightAndModifySelection) +WEBCORE_COMMAND(outdent) +WEBCORE_COMMAND(pageDown) +WEBCORE_COMMAND(pageDownAndModifySelection) +WEBCORE_COMMAND(pageUp) +WEBCORE_COMMAND(pageUpAndModifySelection) +WEBCORE_COMMAND(pasteAsPlainText) +WEBCORE_COMMAND(selectAll) +WEBCORE_COMMAND(selectLine) +WEBCORE_COMMAND(selectParagraph) +WEBCORE_COMMAND(selectSentence) +WEBCORE_COMMAND(selectToMark) +WEBCORE_COMMAND(selectWord) +WEBCORE_COMMAND(setMark) +WEBCORE_COMMAND(subscript) +WEBCORE_COMMAND(superscript) +WEBCORE_COMMAND(swapWithMark) +WEBCORE_COMMAND(transpose) +WEBCORE_COMMAND(underline) +WEBCORE_COMMAND(unscript) +WEBCORE_COMMAND(yank) +WEBCORE_COMMAND(yankAndSelect) + +#undef WEBCORE_COMMAND + +#define COMMAND_PROLOGUE if ([self callDelegateDoCommandBySelectorIfNeeded:_cmd]) return; + +- (IBAction)takeFindStringFromSelection:(id)sender +{ + COMMAND_PROLOGUE + + if (![self _hasSelection]) { + NSBeep(); + return; + } + + [NSPasteboard _web_setFindPasteboardString:[self selectedString] withOwner:self]; +} + +- (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pasteboard types:(NSArray *)types +{ + [pasteboard declareTypes:types owner:[self _topHTMLView]]; + [self writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard]; + return YES; +} + +- (id)validRequestorForSendType:(NSString *)sendType returnType:(NSString *)returnType +{ + BOOL isSendTypeOK = !sendType || ([[self pasteboardTypesForSelection] containsObject:sendType] && [self _hasSelection]); + BOOL isReturnTypeOK = NO; + if (!returnType) + isReturnTypeOK = YES; + else if ([[[self class] _insertablePasteboardTypes] containsObject:returnType] && [self _isEditable]) { + // We can insert strings in any editable context. We can insert other types, like images, only in rich edit contexts. + isReturnTypeOK = [returnType isEqualToString:NSStringPboardType] || [self _canEditRichly]; + } + if (isSendTypeOK && isReturnTypeOK) + return self; + return [[self nextResponder] validRequestorForSendType:sendType returnType:returnType]; +} + +// jumpToSelection is the old name for what AppKit now calls centerSelectionInVisibleArea. Safari +// was using the old jumpToSelection selector in its menu. Newer versions of Safari will use the +// selector centerSelectionInVisibleArea. We'll leave the old selector in place for two reasons: +// (1) Compatibility between older Safari and newer WebKit; (2) other WebKit-based applications +// might be using the selector, and we don't want to break them. +- (void)jumpToSelection:(id)sender +{ + COMMAND_PROLOGUE + + if (Frame* coreFrame = core([self _frame])) + coreFrame->selection()->revealSelection(ScrollAlignment::alignCenterAlways); +} + +- (NSCellStateValue)selectionHasStyle:(CSSStyleDeclaration*)style +{ + Frame* coreFrame = core([self _frame]); + if (!coreFrame) + return NSOffState; + return kit(coreFrame->editor()->selectionHasStyle(style)); +} + +- (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item +{ + SEL action = [item action]; + RefPtr<Frame> frame = core([self _frame]); + + if (!frame) + return NO; + + if (Document* doc = frame->document()) { + if (doc->isPluginDocument()) + return NO; + if (doc->isImageDocument()) { + if (action == @selector(copy:)) + return frame->loader()->isComplete(); + return NO; + } + } + + if (action == @selector(changeSpelling:) + || action == @selector(_changeSpellingFromMenu:) + || action == @selector(checkSpelling:) + || action == @selector(complete:) + || action == @selector(pasteFont:)) + return [self _canEdit]; + + if (action == @selector(showGuessPanel:)) { +#ifndef BUILDING_ON_TIGER + // Match OS X AppKit behavior for post-Tiger. Don't change Tiger behavior. + NSMenuItem *menuItem = (NSMenuItem *)item; + if ([menuItem isKindOfClass:[NSMenuItem class]]) { + BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible]; + [menuItem setTitle:panelShowing + ? UI_STRING("Hide Spelling and Grammar", "menu item title") + : UI_STRING("Show Spelling and Grammar", "menu item title")]; + } +#endif + return [self _canEdit]; + } + + if (action == @selector(changeBaseWritingDirection:) + || action == @selector(makeBaseWritingDirectionLeftToRight:) + || action == @selector(makeBaseWritingDirectionRightToLeft:)) { + NSWritingDirection writingDirection; + + if (action == @selector(changeBaseWritingDirection:)) { + writingDirection = static_cast<NSWritingDirection>([item tag]); + if (writingDirection == NSWritingDirectionNatural) + return NO; + } else if (action == @selector(makeBaseWritingDirectionLeftToRight:)) + writingDirection = NSWritingDirectionLeftToRight; + else + writingDirection = NSWritingDirectionRightToLeft; + + NSMenuItem *menuItem = (NSMenuItem *)item; + if ([menuItem isKindOfClass:[NSMenuItem class]]) { + RefPtr<CSSStyleDeclaration> style = CSSMutableStyleDeclaration::create(); + ExceptionCode ec; + style->setProperty("direction", writingDirection == NSWritingDirectionLeftToRight ? "LTR" : "RTL", ec); + [menuItem setState:frame->editor()->selectionHasStyle(style.get())]; + } + return [self _canEdit]; + } + + if (action == @selector(makeBaseWritingDirectionNatural:)) { + NSMenuItem *menuItem = (NSMenuItem *)item; + if ([menuItem isKindOfClass:[NSMenuItem class]]) + [menuItem setState:NSOffState]; + return NO; + } + + if (action == @selector(toggleBaseWritingDirection:)) { + NSMenuItem *menuItem = (NSMenuItem *)item; + if ([menuItem isKindOfClass:[NSMenuItem class]]) { + RefPtr<CSSStyleDeclaration> style = CSSMutableStyleDeclaration::create(); + ExceptionCode ec; + style->setProperty("direction", "RTL", ec); + // Take control of the title of the menu item instead of just checking/unchecking it because + // a check would be ambiguous. + [menuItem setTitle:frame->editor()->selectionHasStyle(style.get()) + ? UI_STRING("Left to Right", "Left to Right context menu item") + : UI_STRING("Right to Left", "Right to Left context menu item")]; + } + return [self _canEdit]; + } + + if (action == @selector(changeAttributes:) + || action == @selector(changeColor:) + || action == @selector(changeFont:)) + return [self _canEditRichly]; + + if (action == @selector(capitalizeWord:) + || action == @selector(lowercaseWord:) + || action == @selector(uppercaseWord:)) + return [self _hasSelection] && [self _isEditable]; + + if (action == @selector(centerSelectionInVisibleArea:) + || action == @selector(jumpToSelection:) + || action == @selector(copyFont:)) + return [self _hasSelection] || ([self _isEditable] && [self _hasInsertionPoint]); + + if (action == @selector(changeDocumentBackgroundColor:)) + return [[self _webView] isEditable] && [self _canEditRichly]; + + if (action == @selector(_ignoreSpellingFromMenu:) + || action == @selector(_learnSpellingFromMenu:) + || action == @selector(takeFindStringFromSelection:)) + return [self _hasSelection]; + + if (action == @selector(paste:) || action == @selector(pasteAsPlainText:)) + return frame && (frame->editor()->canDHTMLPaste() || frame->editor()->canPaste()); + + if (action == @selector(pasteAsRichText:)) + return frame && (frame->editor()->canDHTMLPaste() + || (frame->editor()->canPaste() && frame->selection()->isContentRichlyEditable())); + + if (action == @selector(performFindPanelAction:)) + return NO; + + if (action == @selector(_lookUpInDictionaryFromMenu:)) + return [self _hasSelection]; + + if (action == @selector(stopSpeaking:)) + return [NSApp isSpeaking]; + +#ifndef BUILDING_ON_TIGER + if (action == @selector(toggleGrammarChecking:)) { + // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must validate + // the selector here because we implement it here, and we must implement it here because the AppKit + // code checks the first responder. + NSMenuItem *menuItem = (NSMenuItem *)item; + if ([menuItem isKindOfClass:[NSMenuItem class]]) + [menuItem setState:[self isGrammarCheckingEnabled] ? NSOnState : NSOffState]; + return YES; + } +#endif + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + if (action == @selector(orderFrontSubstitutionsPanel:)) { + NSMenuItem *menuItem = (NSMenuItem *)item; + if ([menuItem isKindOfClass:[NSMenuItem class]]) { + BOOL panelShowing = [[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible]; + [menuItem setTitle:panelShowing + ? UI_STRING("Hide Substitutions", "menu item title") + : UI_STRING("Show Substitutions", "menu item title")]; + } + return [self _canEdit]; + } + // FIXME 4799134: WebView is the bottleneck for this logic, but we must validate + // the selector here because we implement it here, and we must implement it here because the AppKit + // code checks the first responder. + if (action == @selector(toggleSmartInsertDelete:)) { + NSMenuItem *menuItem = (NSMenuItem *)item; + if ([menuItem isKindOfClass:[NSMenuItem class]]) + [menuItem setState:[self smartInsertDeleteEnabled] ? NSOnState : NSOffState]; + return [self _canEdit]; + } + if (action == @selector(toggleAutomaticQuoteSubstitution:)) { + NSMenuItem *menuItem = (NSMenuItem *)item; + if ([menuItem isKindOfClass:[NSMenuItem class]]) + [menuItem setState:[self isAutomaticQuoteSubstitutionEnabled] ? NSOnState : NSOffState]; + return [self _canEdit]; + } + if (action == @selector(toggleAutomaticLinkDetection:)) { + NSMenuItem *menuItem = (NSMenuItem *)item; + if ([menuItem isKindOfClass:[NSMenuItem class]]) + [menuItem setState:[self isAutomaticLinkDetectionEnabled] ? NSOnState : NSOffState]; + return [self _canEdit]; + } + if (action == @selector(toggleAutomaticDashSubstitution:)) { + NSMenuItem *menuItem = (NSMenuItem *)item; + if ([menuItem isKindOfClass:[NSMenuItem class]]) + [menuItem setState:[self isAutomaticDashSubstitutionEnabled] ? NSOnState : NSOffState]; + return [self _canEdit]; + } + if (action == @selector(toggleAutomaticTextReplacement:)) { + NSMenuItem *menuItem = (NSMenuItem *)item; + if ([menuItem isKindOfClass:[NSMenuItem class]]) + [menuItem setState:[self isAutomaticTextReplacementEnabled] ? NSOnState : NSOffState]; + return [self _canEdit]; + } + if (action == @selector(toggleAutomaticSpellingCorrection:)) { + NSMenuItem *menuItem = (NSMenuItem *)item; + if ([menuItem isKindOfClass:[NSMenuItem class]]) + [menuItem setState:[self isAutomaticSpellingCorrectionEnabled] ? NSOnState : NSOffState]; + return [self _canEdit]; + } +#endif + + Editor::Command command = [self coreCommandBySelector:action]; + if (command.isSupported()) { + NSMenuItem *menuItem = (NSMenuItem *)item; + if ([menuItem isKindOfClass:[NSMenuItem class]]) + [menuItem setState:kit(command.state())]; + return command.isEnabled(); + } + + return YES; +} + +- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item +{ + // This can be called during teardown when _webView is nil. Return NO when this happens, because CallUIDelegateReturningBoolean + // assumes the WebVIew is non-nil. + if (![self _webView]) + return NO; + BOOL result = [self validateUserInterfaceItemWithoutDelegate:item]; + return CallUIDelegateReturningBoolean(result, [self _webView], @selector(webView:validateUserInterfaceItem:defaultValidation:), item, result); +} + +- (BOOL)acceptsFirstResponder +{ + // Don't accept first responder when we first click on this view. + // We have to pass the event down through WebCore first to be sure we don't hit a subview. + // Do accept first responder at any other time, for example from keyboard events, + // or from calls back from WebCore once we begin mouse-down event handling. + NSEvent *event = [NSApp currentEvent]; + if ([event type] == NSLeftMouseDown + && !_private->handlingMouseDownEvent + && NSPointInRect([event locationInWindow], [self convertRect:[self visibleRect] toView:nil])) { + return NO; + } + return YES; +} + +- (BOOL)maintainsInactiveSelection +{ + // This method helps to determine whether the WebHTMLView should maintain + // an inactive selection when it's not first responder. + // Traditionally, these views have not maintained such selections, + // clearing them when the view was not first responder. However, + // to fix bugs like this one: + // <rdar://problem/3672088>: "Editable WebViews should maintain a selection even + // when they're not firstResponder" + // it was decided to add a switch to act more like an NSTextView. + + if ([[self _webView] maintainsInactiveSelection]) + return YES; + + // Predict the case where we are losing first responder status only to + // gain it back again. Want to keep the selection in that case. + id nextResponder = [[self window] _newFirstResponderAfterResigning]; + if ([nextResponder isKindOfClass:[NSScrollView class]]) { + id contentView = [nextResponder contentView]; + if (contentView) + nextResponder = contentView; + } + if ([nextResponder isKindOfClass:[NSClipView class]]) { + id documentView = [nextResponder documentView]; + if (documentView) + nextResponder = documentView; + } + if (nextResponder == self) + return YES; + + Frame* coreFrame = core([self _frame]); + bool selectionIsEditable = coreFrame && coreFrame->selection()->isContentEditable(); + bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]] + && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]]; + + return selectionIsEditable && nextResponderIsInWebView; +} + +- (void)addMouseMovedObserver +{ + if (!_private->dataSource || ![self _isTopHTMLView] || _private->observingMouseMovedNotifications) + return; + + // Unless the Dashboard asks us to do this for all windows, keep an observer going only for the key window. + if (!([[self window] isKeyWindow] +#if ENABLE(DASHBOARD_SUPPORT) + || [[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows] +#endif + )) + return; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mouseMovedNotification:) + name:WKMouseMovedNotification() object:nil]; + [self _frameOrBoundsChanged]; + _private->observingMouseMovedNotifications = true; +} + +- (void)removeMouseMovedObserver +{ +#if ENABLE(DASHBOARD_SUPPORT) + // Don't remove the observer if we're running the Dashboard. + if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows]) + return; +#endif + + [[self _webView] _mouseDidMoveOverElement:nil modifierFlags:0]; + [self _removeMouseMovedObserverUnconditionally]; +} + +- (void)addSuperviewObservers +{ + if (_private->observingSuperviewNotifications) + return; + + NSView *superview = [self superview]; + if (!superview || ![self window]) + return; + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewFrameDidChangeNotification object:superview]; + [notificationCenter addObserver:self selector:@selector(_frameOrBoundsChanged) name:NSViewBoundsDidChangeNotification object:superview]; + + // In addition to registering for frame/bounds change notifications, call -_frameOrBoundsChanged. + // It will check the current scroll against the previous layout's scroll. We need to + // do this here to catch the case where the WebView is laid out at one size, removed from its + // window, resized, and inserted into another window. Our frame/bounds changed notifications + // will not be sent in that situation, since we only watch for changes while in the view hierarchy. + [self _frameOrBoundsChanged]; + + _private->observingSuperviewNotifications = true; +} + +- (void)addWindowObservers +{ + if (_private->observingWindowNotifications) + return; + + NSWindow *window = [self window]; + if (!window) + return; + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:nil]; + [notificationCenter addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:nil]; + [notificationCenter addObserver:self selector:@selector(windowWillClose:) name:NSWindowWillCloseNotification object:window]; + + _private->observingWindowNotifications = true; +} + +- (void)viewWillMoveToSuperview:(NSView *)newSuperview +{ + [self _removeSuperviewObservers]; +} + +- (void)viewDidMoveToSuperview +{ + if ([self superview] != nil) + [self addSuperviewObservers]; + +#if USE(ACCELERATED_COMPOSITING) + if ([self superview] && [self _isUsingAcceleratedCompositing]) { + WebView *webView = [self _webView]; + if ([webView _postsAcceleratedCompositingNotifications]) + [[NSNotificationCenter defaultCenter] postNotificationName:_WebViewDidStartAcceleratedCompositingNotification object:webView userInfo:nil]; + } +#endif +} + +- (void)viewWillMoveToWindow:(NSWindow *)window +{ + // Don't do anything if we aren't initialized. This happens + // when decoding a WebView. When WebViews are decoded their subviews + // are created by initWithCoder: and so won't be normally + // initialized. The stub views are discarded by WebView. + if (!_private) + return; + + // FIXME: Some of these calls may not work because this view may be already removed from it's superview. + [self _removeMouseMovedObserverUnconditionally]; + [self _removeWindowObservers]; + [self _removeSuperviewObservers]; + [self _cancelUpdateMouseoverTimer]; + + // FIXME: This accomplishes the same thing as the call to setCanStartMedia(false) in + // WebView. It would be nice to have a single mechanism instead of two. + [[self _pluginController] stopAllPlugins]; +} + +- (void)viewDidMoveToWindow +{ + // Don't do anything if we aren't initialized. This happens + // when decoding a WebView. When WebViews are decoded their subviews + // are created by initWithCoder: and so won't be normally + // initialized. The stub views are discarded by WebView. + if (!_private || _private->closed) + return; + + [self _stopAutoscrollTimer]; + if ([self window]) { + _private->lastScrollPosition = [[self superview] bounds].origin; + [self addWindowObservers]; + [self addSuperviewObservers]; + [self addMouseMovedObserver]; + + // FIXME: This accomplishes the same thing as the call to setCanStartMedia(true) in + // WebView. It would be nice to have a single mechanism instead of two. + [[self _pluginController] startAllPlugins]; + + _private->lastScrollPosition = NSZeroPoint; + +#if USE(ACCELERATED_COMPOSITING) && !defined(BUILDING_ON_LEOPARD) + // We may have created the layer hosting view while outside the window. Update the scale factor + // now that we have a window to get it from. + if (_private->layerHostingView) { + CGFloat scaleFactor = [[self window] userSpaceScaleFactor]; + [[_private->layerHostingView layer] setTransform:CATransform3DMakeScale(scaleFactor, scaleFactor, 1)]; + } +#endif + } +} + +- (void)_web_makePluginSubviewsPerformSelector:(SEL)selector withObject:(id)object +{ +#if ENABLE(NETSCAPE_PLUGIN_API) + // Copy subviews because [self subviews] returns the view's mutable internal array, + // and we must avoid mutating the array while enumerating it. + NSArray *subviews = [[self subviews] copy]; + + NSEnumerator *enumerator = [subviews objectEnumerator]; + WebNetscapePluginView *view; + while ((view = [enumerator nextObject]) != nil) + if ([view isKindOfClass:[WebBaseNetscapePluginView class]]) + [view performSelector:selector withObject:object]; + + [subviews release]; +#endif +} + +- (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow +{ + [self _web_makePluginSubviewsPerformSelector:@selector(viewWillMoveToHostWindow:) withObject:hostWindow]; +} + +- (void)viewDidMoveToHostWindow +{ + [self _web_makePluginSubviewsPerformSelector:@selector(viewDidMoveToHostWindow) withObject:nil]; +} + + +- (void)addSubview:(NSView *)view +{ + [super addSubview:view]; + + if ([WebPluginController isPlugInView:view]) + [[self _pluginController] addPlugin:view]; +} + +- (void)willRemoveSubview:(NSView *)subview +{ +#ifndef NDEBUG + // Have to null-check _private, since this can be called via -dealloc when + // cleaning up the the layerHostingView. + if (_private && _private->enumeratingSubviews) + LOG(View, "A view of class %s was removed during subview enumeration for layout or printing mode change. We will still do layout or the printing mode change even though this view is no longer in the view hierarchy.", object_getClassName([subview class])); +#endif + + if ([WebPluginController isPlugInView:subview]) + [[self _pluginController] destroyPlugin:subview]; + + [super willRemoveSubview:subview]; +} + +- (void)reapplyStyles +{ +#ifdef LOG_TIMES + double start = CFAbsoluteTimeGetCurrent(); +#endif + + if (Frame* coreFrame = core([self _frame])) + coreFrame->document()->styleSelectorChanged(RecalcStyleImmediately); + +#ifdef LOG_TIMES + double thisTime = CFAbsoluteTimeGetCurrent() - start; + LOG(Timing, "%s apply style seconds = %f", [self URL], thisTime); +#endif +} + +// Do a layout, but set up a new fixed width for the purposes of doing printing layout. +// minPageWidth==0 implies a non-printing layout +- (void)layoutToMinimumPageWidth:(float)minPageWidth height:(float)minPageHeight maximumPageWidth:(float)maxPageWidth adjustingViewSize:(BOOL)adjustViewSize +{ + if (![self _needsLayout]) + return; + +#ifdef LOG_TIMES + double start = CFAbsoluteTimeGetCurrent(); +#endif + + LOG(View, "%@ doing layout", self); + + Frame* coreFrame = core([self _frame]); + if (!coreFrame) + return; + + if (FrameView* coreView = coreFrame->view()) { + if (minPageWidth > 0.0) + coreView->forceLayoutForPagination(FloatSize(minPageWidth, minPageHeight), maxPageWidth / minPageWidth, adjustViewSize ? Frame::AdjustViewSize : Frame::DoNotAdjustViewSize); + else { + coreView->forceLayout(!adjustViewSize); + if (adjustViewSize) + coreView->adjustViewSize(); + } + } + +#ifdef LOG_TIMES + double thisTime = CFAbsoluteTimeGetCurrent() - start; + LOG(Timing, "%s layout seconds = %f", [self URL], thisTime); +#endif +} + +- (void)layout +{ + [self layoutToMinimumPageWidth:0 height:0 maximumPageWidth:0 adjustingViewSize:NO]; +} + +// Deliver mouseup events to the DOM for button 2. +- (void)rightMouseUp:(NSEvent *)event +{ + // There's a chance that if we run a nested event loop the event will be released. + // Retaining and then autoreleasing prevents that from causing a problem later here or + // inside AppKit code. + [[event retain] autorelease]; + + [super rightMouseUp:event]; + + if (Frame* coreframe = core([self _frame])) + coreframe->eventHandler()->mouseUp(event); +} + +static void setMenuItemTarget(NSMenuItem* menuItem) +{ + // Don't set the menu item's action to the context menu action forwarder if we already + // have an action. + if ([menuItem action]) + return; + + [menuItem setTarget:[WebMenuTarget sharedMenuTarget]]; + [menuItem setAction:@selector(forwardContextMenuAction:)]; +} + +static void setMenuTargets(NSMenu* menu) +{ + NSInteger itemCount = [menu numberOfItems]; + for (NSInteger i = 0; i < itemCount; ++i) { + NSMenuItem *item = [menu itemAtIndex:i]; + setMenuItemTarget(item); + if ([item hasSubmenu]) + setMenuTargets([item submenu]); + } +} + +- (NSMenu *)menuForEvent:(NSEvent *)event +{ + // There's a chance that if we run a nested event loop the event will be released. + // Retaining and then autoreleasing prevents that from causing a problem later here or + // inside AppKit code. + [[event retain] autorelease]; + + [_private->completionController endRevertingChange:NO moveLeft:NO]; + + RefPtr<Frame> coreFrame = core([self _frame]); + if (!coreFrame) + return nil; + + Page* page = coreFrame->page(); + if (!page) + return nil; + + // Match behavior of other browsers by sending a mousedown event for right clicks. + _private->handlingMouseDownEvent = YES; + page->contextMenuController()->clearContextMenu(); + coreFrame->eventHandler()->mouseDown(event); + BOOL handledEvent = coreFrame->eventHandler()->sendContextMenuEvent(event); + _private->handlingMouseDownEvent = NO; + + if (!handledEvent) + return nil; + + // Re-get page, since it might have gone away during event handling. + page = coreFrame->page(); + if (!page) + return nil; + + ContextMenu* coreMenu = page->contextMenuController()->contextMenu(); + if (!coreMenu) + return nil; + + NSArray* menuItems = coreMenu->platformDescription(); + if (!menuItems) + return nil; + + NSUInteger count = [menuItems count]; + if (!count) + return nil; + + NSMenu* menu = [[[NSMenu alloc] init] autorelease]; + for (NSUInteger i = 0; i < count; i++) + [menu addItem:[menuItems objectAtIndex:i]]; + setMenuTargets(menu); + + [[WebMenuTarget sharedMenuTarget] setMenuController:page->contextMenuController()]; + + return menu; +} + +- (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)clearFocus +{ + Frame* coreFrame = core([self _frame]); + if (!coreFrame) + return; + Document* document = coreFrame->document(); + if (!document) + return; + + document->setFocusedNode(0); +} + +- (BOOL)isOpaque +{ + return [[self _webView] drawsBackground]; +} + +#if !LOG_DISABLED +- (void)setNeedsDisplay:(BOOL)flag +{ + LOG(View, "%@ setNeedsDisplay:%@", self, flag ? @"YES" : @"NO"); + [super setNeedsDisplay:flag]; +} +#endif + +#ifndef BUILDING_ON_TIGER +- (void)setNeedsDisplayInRect:(NSRect)invalidRect +{ + if (_private->inScrollPositionChanged) { + // When scrolling, the dirty regions are adjusted for the scroll only + // after NSViewBoundsDidChangeNotification is sent. Translate the invalid + // rect to pre-scrolled coordinates in order to get the right dirty region + // after adjustment. See <rdar://problem/7678927>. + NSPoint origin = [[self superview] bounds].origin; + invalidRect.origin.x -= _private->lastScrollPosition.x - origin.x; + invalidRect.origin.y -= _private->lastScrollPosition.y - origin.y; + } + [super setNeedsDisplayInRect:invalidRect]; +} +#endif + +- (void)setNeedsLayout: (BOOL)flag +{ + LOG(View, "%@ setNeedsLayout:%@", self, flag ? @"YES" : @"NO"); + if (!flag) + return; // There's no way to say you don't need a layout. + if (Frame* frame = core([self _frame])) { + if (frame->document() && frame->document()->inPageCache()) + return; + if (FrameView* view = frame->view()) + view->setNeedsLayout(); + } +} + +- (void)setNeedsToApplyStyles: (BOOL)flag +{ + LOG(View, "%@ setNeedsToApplyStyles:%@", self, flag ? @"YES" : @"NO"); + if (!flag) + return; // There's no way to say you don't need a style recalc. + if (Frame* frame = core([self _frame])) { + if (frame->document() && frame->document()->inPageCache()) + return; + frame->document()->scheduleForcedStyleRecalc(); + } +} + +- (void)drawSingleRect:(NSRect)rect +{ + [NSGraphicsContext saveGraphicsState]; + NSRectClip(rect); + + ASSERT([[self superview] isKindOfClass:[WebClipView class]]); + + [(WebClipView *)[self superview] setAdditionalClip:rect]; + + @try { + if ([self _transparentBackground]) { + [[NSColor clearColor] set]; + NSRectFill (rect); + } + + [[self _frame] _drawRect:rect contentsOnly:YES]; + + WebView *webView = [self _webView]; + + // This hack is needed for <rdar://problem/5023545>. We can hit a race condition where drawRect will be + // called after the WebView has closed. If the client did not properly close the WebView and set the + // UIDelegate to nil, then the UIDelegate will be stale and this code will crash. + static BOOL version3OrLaterClient = WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_QUICKBOOKS_QUIRK); + if (version3OrLaterClient) + [[webView _UIDelegateForwarder] webView:webView didDrawRect:[webView convertRect:rect fromView:self]]; + + if (WebNodeHighlight *currentHighlight = [webView currentNodeHighlight]) + [currentHighlight setNeedsUpdateInTargetViewRect:[self convertRect:rect toView:[currentHighlight targetView]]]; + + [(WebClipView *)[self superview] resetAdditionalClip]; + + [NSGraphicsContext restoreGraphicsState]; + } @catch (NSException *localException) { + [(WebClipView *)[self superview] resetAdditionalClip]; + [NSGraphicsContext restoreGraphicsState]; + LOG_ERROR("Exception caught while drawing: %@", localException); + [localException raise]; + } +} + +- (void)drawRect:(NSRect)rect +{ + ASSERT_MAIN_THREAD(); + LOG(View, "%@ drawing", self); + + const NSRect *rects; + NSInteger count; + [self getRectsBeingDrawn:&rects count:&count]; + + BOOL subviewsWereSetAside = _private->subviewsSetAside; + if (subviewsWereSetAside) + [self _restoreSubviews]; + +#ifdef LOG_TIMES + double start = CFAbsoluteTimeGetCurrent(); +#endif + + WebView *webView = [self _webView]; + if ([webView _mustDrawUnionedRect:rect singleRects:rects count:count]) + [self drawSingleRect:rect]; + else + for (int i = 0; i < count; ++i) + [self drawSingleRect:rects[i]]; + +#ifdef LOG_TIMES + double thisTime = CFAbsoluteTimeGetCurrent() - start; + LOG(Timing, "%s draw seconds = %f", widget->part()->baseURL().URL().latin1(), thisTime); +#endif + + if (subviewsWereSetAside) + [self _setAsideSubviews]; + +#if USE(ACCELERATED_COMPOSITING) + // Only do the synchronization dance if we're drawing into the window, otherwise + // we risk disabling screen updates when no flush is pending. + if ([NSGraphicsContext currentContext] == [[self window] graphicsContext] && [webView _needsOneShotDrawingSynchronization]) { + // Disable screen updates to minimize the chances of the race between the CA + // display link and AppKit drawing causing flashes. + [[self window] disableScreenUpdatesUntilFlush]; + + // Make sure any layer changes that happened as a result of layout + // via -viewWillDraw are committed. + [CATransaction flush]; + [webView _setNeedsOneShotDrawingSynchronization:NO]; + } +#endif + + if (webView) + CallUIDelegate(webView, @selector(webView:didDrawFrame:), [self _frame]); +} + +// Turn off the additional clip while computing our visibleRect. +- (NSRect)visibleRect +{ + if (!([[self superview] isKindOfClass:[WebClipView class]])) + return [super visibleRect]; + + WebClipView *clipView = (WebClipView *)[self superview]; + + BOOL hasAdditionalClip = [clipView hasAdditionalClip]; + if (!hasAdditionalClip) { + return [super visibleRect]; + } + + NSRect additionalClip = [clipView additionalClip]; + [clipView resetAdditionalClip]; + NSRect visibleRect = [super visibleRect]; + [clipView setAdditionalClip:additionalClip]; + return visibleRect; +} + +- (void)_invalidateGStatesForTree +{ + // AppKit is in the process of traversing the NSView tree, and is going to send -renewGState to + // descendants, including plug-in views. This can result in calls out to plug-in code and back into + // WebCore via JavaScript, which could normally mutate the NSView tree while it is being traversed. + // Defer those mutations while descendants are being traveresed. + RenderWidget::suspendWidgetHierarchyUpdates(); + [super _invalidateGStatesForTree]; + RenderWidget::resumeWidgetHierarchyUpdates(); +} + +- (BOOL)isFlipped +{ + return YES; +} + +- (void)windowDidBecomeKey:(NSNotification *)notification +{ + if (!pthread_main_np()) { + [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO]; + return; + } + + NSWindow *keyWindow = [notification object]; + + if (keyWindow == [self window]) + [self addMouseMovedObserver]; +} + +- (void)windowDidResignKey:(NSNotification *)notification +{ + if (!pthread_main_np()) { + [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO]; + return; + } + + NSWindow *formerKeyWindow = [notification object]; + + if (formerKeyWindow == [self window]) + [self removeMouseMovedObserver]; + + if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) + [_private->completionController endRevertingChange:NO moveLeft:NO]; +} + +- (void)windowWillClose:(NSNotification *)notification +{ + if (!pthread_main_np()) { + [self performSelectorOnMainThread:_cmd withObject:notification waitUntilDone:NO]; + return; + } + + [_private->completionController endRevertingChange:NO moveLeft:NO]; + [[self _pluginController] destroyAllPlugins]; +} + +- (void)scrollWheel:(NSEvent *)event +{ + // There's a chance that responding to this event will run a nested event loop, and + // fetching a new event might release the old one. Retaining and then autoreleasing + // the current event prevents that from causing a problem inside WebKit or AppKit code. + [[event retain] autorelease]; + + Frame* frame = core([self _frame]); + if (!frame || !frame->eventHandler()->wheelEvent(event)) + [super scrollWheel:event]; +} + +- (BOOL)_isSelectionEvent:(NSEvent *)event +{ + NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; + return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsSelectedKey] boolValue]; +} + +- (BOOL)_isScrollBarEvent:(NSEvent *)event +{ + NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil]; + return [[[self elementAtPoint:point allowShadowContent:YES] objectForKey:WebElementIsInScrollBarKey] boolValue]; +} + +- (BOOL)acceptsFirstMouse:(NSEvent *)event +{ + // There's a chance that responding to this event will run a nested event loop, and + // fetching a new event might release the old one. Retaining and then autoreleasing + // the current event prevents that from causing a problem inside WebKit or AppKit code. + [[event retain] autorelease]; + + NSView *hitView = [self _hitViewForEvent:event]; + WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil; + +#if ENABLE(DASHBOARD_SUPPORT) + if ([[self _webView] _dashboardBehavior:WebDashboardBehaviorAlwaysAcceptsFirstMouse]) + return YES; +#endif + + if (hitHTMLView) { + bool result = false; + if (Frame* coreFrame = core([hitHTMLView _frame])) { + coreFrame->eventHandler()->setActivationEventNumber([event eventNumber]); + [hitHTMLView _setMouseDownEvent:event]; + if ([hitHTMLView _isSelectionEvent:event]) + result = coreFrame->eventHandler()->eventMayStartDrag(event); + else if ([hitHTMLView _isScrollBarEvent:event]) + result = true; + [hitHTMLView _setMouseDownEvent:nil]; + } + return result; + } + return [hitView acceptsFirstMouse:event]; +} + +- (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event +{ + // There's a chance that responding to this event will run a nested event loop, and + // fetching a new event might release the old one. Retaining and then autoreleasing + // the current event prevents that from causing a problem inside WebKit or AppKit code. + [[event retain] autorelease]; + + NSView *hitView = [self _hitViewForEvent:event]; + WebHTMLView *hitHTMLView = [hitView isKindOfClass:[self class]] ? (WebHTMLView *)hitView : nil; + if (hitHTMLView) { + bool result = false; + if ([hitHTMLView _isSelectionEvent:event]) { + if (Frame* coreFrame = core([hitHTMLView _frame])) { + [hitHTMLView _setMouseDownEvent:event]; + result = coreFrame->eventHandler()->eventMayStartDrag(event); + [hitHTMLView _setMouseDownEvent:nil]; + } + } + return result; + } + return [hitView shouldDelayWindowOrderingForEvent:event]; +} + +- (void)mouseDown:(NSEvent *)event +{ + // There's a chance that responding to this event will run a nested event loop, and + // fetching a new event might release the old one. Retaining and then autoreleasing + // the current event prevents that from causing a problem inside WebKit or AppKit code. + [[event retain] autorelease]; + + RetainPtr<WebHTMLView> protector = self; + if ([[self inputContext] wantsToHandleMouseEvents] && [[self inputContext] handleMouseEvent:event]) + return; + + _private->handlingMouseDownEvent = YES; + + // Record the mouse down position so we can determine drag hysteresis. + [self _setMouseDownEvent:event]; + + NSInputManager *currentInputManager = [NSInputManager currentInputManager]; + if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event]) + goto done; + + [_private->completionController endRevertingChange:NO moveLeft:NO]; + + // If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here. + // We don't want to pass them along to KHTML a second time. + if (!([event modifierFlags] & NSControlKeyMask)) { + _private->ignoringMouseDraggedEvents = NO; + + // Don't do any mouseover while the mouse is down. + [self _cancelUpdateMouseoverTimer]; + + // Let WebCore get a chance to deal with the event. This will call back to us + // to start the autoscroll timer if appropriate. + if (Frame* coreframe = core([self _frame])) + coreframe->eventHandler()->mouseDown(event); + } + +done: + _private->handlingMouseDownEvent = NO; +} + +- (void)dragImage:(NSImage *)dragImage + at:(NSPoint)at + offset:(NSSize)offset + event:(NSEvent *)event + pasteboard:(NSPasteboard *)pasteboard + source:(id)source + slideBack:(BOOL)slideBack +{ + ASSERT(self == [self _topHTMLView]); + [super dragImage:dragImage at:at offset:offset event:event pasteboard:pasteboard source:source slideBack:slideBack]; +} + +- (void)mouseDragged:(NSEvent *)event +{ + // There's a chance that responding to this event will run a nested event loop, and + // fetching a new event might release the old one. Retaining and then autoreleasing + // the current event prevents that from causing a problem inside WebKit or AppKit code. + [[event retain] autorelease]; + + NSInputManager *currentInputManager = [NSInputManager currentInputManager]; + if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event]) + return; + + [self retain]; + + if (!_private->ignoringMouseDraggedEvents) { + if (Frame* frame = core([self _frame])) { + if (Page* page = frame->page()) + page->mainFrame()->eventHandler()->mouseDragged(event); + } + } + + [self release]; +} + +- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal +{ + ASSERT(![self _webView] || [self _isTopHTMLView]); + + Page* page = core([self _webView]); + if (!page) + return NSDragOperationNone; + + return (NSDragOperation)page->dragController()->sourceDragOperation(); +} + +- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation +{ + ASSERT(![self _webView] || [self _isTopHTMLView]); + + NSPoint windowImageLoc = [[self window] convertScreenToBase:aPoint]; + NSPoint windowMouseLoc = windowImageLoc; + + if (Page* page = core([self _webView])) { + DragController* dragController = page->dragController(); + windowMouseLoc = NSMakePoint(windowImageLoc.x + dragController->dragOffset().x(), windowImageLoc.y + dragController->dragOffset().y()); + dragController->dragEnded(); + } + + [[self _frame] _dragSourceEndedAt:windowMouseLoc operation:operation]; + + // Prevent queued mouseDragged events from coming after the drag and fake mouseUp event. + _private->ignoringMouseDraggedEvents = YES; + + // Once the dragging machinery kicks in, we no longer get mouse drags or the up event. + // WebCore expects to get balanced down/up's, so we must fake up a mouseup. + NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSLeftMouseUp + location:windowMouseLoc + modifierFlags:[[NSApp currentEvent] modifierFlags] + timestamp:[NSDate timeIntervalSinceReferenceDate] + windowNumber:[[self window] windowNumber] + context:[[NSApp currentEvent] context] + eventNumber:0 clickCount:0 pressure:0]; + [self mouseUp:fakeEvent]; // This will also update the mouseover state. +} + +- (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination +{ + NSFileWrapper *wrapper = nil; + NSURL *draggingImageURL = nil; + + if (WebCore::CachedImage* tiffResource = [self promisedDragTIFFDataSource]) { + + SharedBuffer *buffer = static_cast<CachedResource*>(tiffResource)->data(); + if (!buffer) + goto noPromisedData; + + NSData *data = buffer->createNSData(); + NSURLResponse *response = tiffResource->response().nsURLResponse(); + draggingImageURL = [response URL]; + wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:data] autorelease]; + NSString* filename = [response suggestedFilename]; + NSString* trueExtension(tiffResource->image()->filenameExtension()); + if (!matchesExtensionOrEquivalent(filename, trueExtension)) + filename = [[filename stringByAppendingString:@"."] stringByAppendingString:trueExtension]; + [wrapper setPreferredFilename:filename]; + } + +noPromisedData: + + if (!wrapper) { + ASSERT(![self _webView] || [self _isTopHTMLView]); + Page* page = core([self _webView]); + + //If a load occurs midway through a drag, the view may be detached, which gives + //us no ability to get to the original Page, so we cannot access any drag state + //FIXME: is there a way to recover? + if (!page) + return nil; + + const KURL& imageURL = page->dragController()->draggingImageURL(); + ASSERT(!imageURL.isEmpty()); + draggingImageURL = imageURL; + + wrapper = [[self _dataSource] _fileWrapperForURL:draggingImageURL]; + } + + if (wrapper == nil) { + LOG_ERROR("Failed to create image file."); + return nil; + } + + // FIXME: Report an error if we fail to create a file. + NSString *path = [[dropDestination path] stringByAppendingPathComponent:[wrapper preferredFilename]]; + path = [[NSFileManager defaultManager] _webkit_pathWithUniqueFilenameForPath:path]; + if (![wrapper writeToFile:path atomically:NO updateFilenames:YES]) + LOG_ERROR("Failed to create image file via -[NSFileWrapper writeToFile:atomically:updateFilenames:]"); + + if (draggingImageURL) + [[NSFileManager defaultManager] _webkit_setMetadataURL:[draggingImageURL absoluteString] referrer:nil atPath:path]; + + return [NSArray arrayWithObject:[path lastPathComponent]]; +} + +- (void)mouseUp:(NSEvent *)event +{ + // There's a chance that responding to this event will run a nested event loop, and + // fetching a new event might release the old one. Retaining and then autoreleasing + // the current event prevents that from causing a problem inside WebKit or AppKit code. + [[event retain] autorelease]; + + [self _setMouseDownEvent:nil]; + + NSInputManager *currentInputManager = [NSInputManager currentInputManager]; + if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event]) + return; + + [self retain]; + + [self _stopAutoscrollTimer]; + if (Frame* frame = core([self _frame])) { + if (Page* page = frame->page()) + page->mainFrame()->eventHandler()->mouseUp(event); + } + [self _updateMouseoverWithFakeEvent]; + + [self release]; +} + +- (void)mouseMovedNotification:(NSNotification *)notification +{ + [self _updateMouseoverWithEvent:[[notification userInfo] objectForKey:@"NSEvent"]]; +} + +// returning YES from this method is the way we tell AppKit that it is ok for this view +// to be in the key loop even when "tab to all controls" is not on. +- (BOOL)needsPanelToBecomeKey +{ + return YES; +} + +// Utility function to make sure we don't return anything through the NSTextInput +// API when an editable region is not currently focused. +static BOOL isTextInput(Frame* coreFrame) +{ + return coreFrame && !coreFrame->selection()->isNone() && coreFrame->selection()->isContentEditable(); +} + +static BOOL isInPasswordField(Frame* coreFrame) +{ + return coreFrame && coreFrame->selection()->isInPasswordField(); +} + +- (BOOL)becomeFirstResponder +{ + NSSelectionDirection direction = NSDirectSelection; + if (![[self _webView] _isPerformingProgrammaticFocus]) + direction = [[self window] keyViewSelectionDirection]; + + [self _updateFontPanel]; + + Frame* frame = core([self _frame]); + if (!frame) + return YES; + + BOOL exposeInputContext = isTextInput(frame) && !isInPasswordField(frame); + if (exposeInputContext != _private->exposeInputContext) { + _private->exposeInputContext = exposeInputContext; + [NSApp updateWindows]; + } + + frame->editor()->setStartNewKillRingSequence(true); + + Page* page = frame->page(); + if (!page) + return YES; + + if (![[self _webView] _isPerformingProgrammaticFocus]) + page->focusController()->setFocusedFrame(frame); + + page->focusController()->setFocused(true); + + if (direction == NSDirectSelection) + return YES; + + if (Document* document = frame->document()) + document->setFocusedNode(0); + page->focusController()->setInitialFocus(direction == NSSelectingNext ? FocusDirectionForward : FocusDirectionBackward, + frame->eventHandler()->currentKeyboardEvent().get()); + return YES; +} + +- (BOOL)resignFirstResponder +{ + BOOL resign = [super resignFirstResponder]; + if (resign) { + [_private->completionController endRevertingChange:NO moveLeft:NO]; + Frame* coreFrame = core([self _frame]); + if (!coreFrame) + return resign; + Page* page = coreFrame->page(); + if (!page) + return resign; + if (![self maintainsInactiveSelection]) { + [self deselectAll]; + if (![[self _webView] _isPerformingProgrammaticFocus]) + [self clearFocus]; + } + + id nextResponder = [[self window] _newFirstResponderAfterResigning]; + bool nextResponderIsInWebView = [nextResponder isKindOfClass:[NSView class]] + && [nextResponder isDescendantOf:[[[self _webView] mainFrame] frameView]]; + if (!nextResponderIsInWebView) + page->focusController()->setFocused(false); + } + return resign; +} + +- (void)setDataSource:(WebDataSource *)dataSource +{ + ASSERT(dataSource); + if (_private->dataSource != dataSource) { + ASSERT(!_private->closed); + BOOL hadDataSource = _private->dataSource != nil; + + [dataSource retain]; + [_private->dataSource release]; + _private->dataSource = dataSource; + [_private->pluginController setDataSource:dataSource]; + + if (!hadDataSource) + [self addMouseMovedObserver]; + } +} + +- (void)dataSourceUpdated:(WebDataSource *)dataSource +{ +} + +// This is an override of an NSControl method that wants to repaint the entire view when the window resigns/becomes +// key. WebHTMLView is an NSControl only because it hosts NSCells that are painted by WebCore's Aqua theme +// renderer (and those cells must be hosted by an enclosing NSControl in order to paint properly). +- (void)updateCell:(NSCell*)cell +{ +} + +// Does setNeedsDisplay:NO as a side effect when printing is ending. +// pageWidth != 0 implies we will relayout to a new width +- (void)_setPrinting:(BOOL)printing minimumPageWidth:(float)minPageWidth height:(float)minPageHeight maximumPageWidth:(float)maxPageWidth adjustViewSize:(BOOL)adjustViewSize paginateScreenContent:(BOOL)paginateScreenContent +{ + if (printing == _private->printing && paginateScreenContent == _private->paginateScreenContent) + return; + + WebFrame *frame = [self _frame]; + NSArray *subframes = [frame childFrames]; + unsigned n = [subframes count]; + unsigned i; + for (i = 0; i != n; ++i) { + WebFrame *subframe = [subframes objectAtIndex:i]; + WebFrameView *frameView = [subframe frameView]; + if ([[subframe _dataSource] _isDocumentHTML]) { + [(WebHTMLView *)[frameView documentView] _setPrinting:printing minimumPageWidth:0 height:0 maximumPageWidth:0 adjustViewSize:adjustViewSize paginateScreenContent:paginateScreenContent]; + } + } + + [_private->pageRects release]; + _private->pageRects = nil; + _private->printing = printing; + _private->paginateScreenContent = paginateScreenContent; + if (!printing && !paginateScreenContent) + _private->avoidingPrintOrphan = NO; + + Frame* coreFrame = core([self _frame]); + if (coreFrame) { + if (FrameView* coreView = coreFrame->view()) + coreView->setMediaType(_private->printing ? "print" : "screen"); + if (Document* document = coreFrame->document()) { + document->setPaginatedForScreen(_private->paginateScreenContent); + document->setPrinting(_private->printing); + document->styleSelectorChanged(RecalcStyleImmediately); + } + } + + [self setNeedsLayout:YES]; + [self layoutToMinimumPageWidth:minPageWidth height:minPageHeight maximumPageWidth:maxPageWidth adjustingViewSize:adjustViewSize]; + if (!printing) { + // Can't do this when starting printing or nested printing won't work, see 3491427. + [self setNeedsDisplay:NO]; + } +} + +- (BOOL)canPrintHeadersAndFooters +{ + return YES; +} + +// This is needed for the case where the webview is embedded in the view that's being printed. +// It shouldn't be called when the webview is being printed directly. +- (void)adjustPageHeightNew:(CGFloat *)newBottom top:(CGFloat)oldTop bottom:(CGFloat)oldBottom limit:(CGFloat)bottomLimit +{ + // This helps when we print as part of a larger print process. + // If the WebHTMLView itself is what we're printing, then we will never have to do this. + BOOL wasInPrintingMode = _private->printing; + if (!wasInPrintingMode) + [self _setPrinting:YES minimumPageWidth:0 height:0 maximumPageWidth:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]]; + + *newBottom = [self _adjustedBottomOfPageWithTop:oldTop bottom:oldBottom limit:bottomLimit]; + + if (!wasInPrintingMode) { + NSPrintOperation *currenPrintOperation = [NSPrintOperation currentOperation]; + if (currenPrintOperation) + // delay _setPrinting:NO until back to main loop as this method may get called repeatedly + [self performSelector:@selector(_delayedEndPrintMode:) withObject:currenPrintOperation afterDelay:0]; + else + // not sure if this is actually ever invoked, it probably shouldn't be + [self _setPrinting:NO minimumPageWidth:0 height:0 maximumPageWidth:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]]; + } +} + +- (float)_scaleFactorForPrintOperation:(NSPrintOperation *)printOperation +{ + float viewWidth = NSWidth([self bounds]); + if (viewWidth < 1) { + LOG_ERROR("%@ has no width when printing", self); + return 1.0f; + } + + float userScaleFactor = [printOperation _web_pageSetupScaleFactor]; + float maxShrinkToFitScaleFactor = 1.0f / _WebHTMLViewPrintingMaximumShrinkFactor; + float shrinkToFitScaleFactor = [printOperation _web_availablePaperWidth] / viewWidth; + float shrinkToAvoidOrphan = _private->avoidingPrintOrphan ? (1.0f / PrintingOrphanShrinkAdjustment) : 1.0f; + return userScaleFactor * max(maxShrinkToFitScaleFactor, shrinkToFitScaleFactor) * shrinkToAvoidOrphan; +} + +// FIXME 3491344: This is a secret AppKit-internal method that we need to override in order +// to get our shrink-to-fit to work with a custom pagination scheme. We can do this better +// if AppKit makes it SPI/API. +- (CGFloat)_provideTotalScaleFactorForPrintOperation:(NSPrintOperation *)printOperation +{ + return [self _scaleFactorForPrintOperation:printOperation]; +} + +// This is used for Carbon printing. At some point we might want to make this public API. +- (void)setPageWidthForPrinting:(float)pageWidth +{ + [self _setPrinting:NO minimumPageWidth:0 height:0 maximumPageWidth:0 adjustViewSize:NO paginateScreenContent:[self _isInScreenPaginationMode]]; + [self _setPrinting:YES minimumPageWidth:pageWidth height:0 maximumPageWidth:pageWidth adjustViewSize:YES paginateScreenContent:[self _isInScreenPaginationMode]]; +} + +- (void)_endPrintModeAndRestoreWindowAutodisplay +{ + [self _endPrintMode]; + [[self window] setAutodisplay:YES]; +} + +- (void)_delayedEndPrintMode:(NSPrintOperation *)initiatingOperation +{ + ASSERT_ARG(initiatingOperation, initiatingOperation != nil); + NSPrintOperation *currentOperation = [NSPrintOperation currentOperation]; + if (initiatingOperation == currentOperation) { + // The print operation is still underway. We don't expect this to ever happen, hence the assert, but we're + // being extra paranoid here since the printing code is so fragile. Delay the cleanup + // further. + ASSERT_NOT_REACHED(); + [self performSelector:@selector(_delayedEndPrintMode:) withObject:initiatingOperation afterDelay:0]; + } else if ([currentOperation view] == self) { + // A new print job has started, but it is printing the same WebHTMLView again. We don't expect + // this to ever happen, hence the assert, but we're being extra paranoid here since the printing code is so + // fragile. Do nothing, because we don't want to break the print job currently in progress, and + // the print job currently in progress is responsible for its own cleanup. + ASSERT_NOT_REACHED(); + } else { + // The print job that kicked off this delayed call has finished, and this view is not being + // printed again. We expect that no other print job has started. Since this delayed call wasn't + // cancelled, beginDocument and endDocument must not have been called, and we need to clean up + // the print mode here. + ASSERT(currentOperation == nil); + [self _endPrintModeAndRestoreWindowAutodisplay]; + } +} + +// Return the number of pages available for printing +- (BOOL)knowsPageRange:(NSRangePointer)range +{ + // Must do this explicit display here, because otherwise the view might redisplay while the print + // sheet was up, using printer fonts (and looking different). + [self displayIfNeeded]; + [[self window] setAutodisplay:NO]; + + [[self _webView] _adjustPrintingMarginsForHeaderAndFooter]; + NSPrintOperation *printOperation = [NSPrintOperation currentOperation]; + if (![self _beginPrintModeWithPageWidth:[printOperation _web_availablePaperWidth] height:[printOperation _web_availablePaperHeight] shrinkToFit:YES]) + return NO; + + // Certain types of errors, including invalid page ranges, can cause beginDocument and + // endDocument to be skipped after we've put ourselves in print mode (see 4145905). In those cases + // we need to get out of print mode without relying on any more callbacks from the printing mechanism. + // If we get as far as beginDocument without trouble, then this delayed request will be cancelled. + // If not cancelled, this delayed call will be invoked in the next pass through the main event loop, + // which is after beginDocument and endDocument would be called. + [self performSelector:@selector(_delayedEndPrintMode:) withObject:printOperation afterDelay:0]; + + // There is a theoretical chance that someone could do some drawing between here and endDocument, + // if something caused setNeedsDisplay after this point. If so, it's not a big tragedy, because + // you'd simply see the printer fonts on screen. As of this writing, this does not happen with Safari. + + range->location = 1; + float totalScaleFactor = [self _scaleFactorForPrintOperation:printOperation]; + float userScaleFactor = [printOperation _web_pageSetupScaleFactor]; + [_private->pageRects release]; + float fullPageHeight = floorf([printOperation _web_availablePaperHeight] / totalScaleFactor); + WebFrame *frame = [self _frame]; + NSArray *newPageRects = [frame _computePageRectsWithPrintWidthScaleFactor:userScaleFactor printHeight:fullPageHeight]; + + // AppKit gets all messed up if you give it a zero-length page count (see 3576334), so if we + // hit that case we'll pass along a degenerate 1 pixel square to print. This will print + // a blank page (with correct-looking header and footer if that option is on), which matches + // the behavior of IE and Camino at least. + if ([newPageRects count] == 0) + newPageRects = [NSArray arrayWithObject:[NSValue valueWithRect:NSMakeRect(0, 0, 1, 1)]]; + else if ([newPageRects count] > 1) { + // If the last page is a short orphan, try adjusting the print height slightly to see if this will squeeze the + // content onto one fewer page. If it does, use the adjusted scale. If not, use the original scale. + float lastPageHeight = NSHeight([[newPageRects lastObject] rectValue]); + if (lastPageHeight/fullPageHeight < LastPrintedPageOrphanRatio) { + NSArray *adjustedPageRects = [frame _computePageRectsWithPrintWidthScaleFactor:userScaleFactor printHeight:fullPageHeight * PrintingOrphanShrinkAdjustment]; + // Use the adjusted rects only if the page count went down + if ([adjustedPageRects count] < [newPageRects count]) { + newPageRects = adjustedPageRects; + _private->avoidingPrintOrphan = YES; + } + } + } + + _private->pageRects = [newPageRects retain]; + + range->length = [_private->pageRects count]; + + return YES; +} + +// Return the drawing rectangle for a particular page number +- (NSRect)rectForPage:(NSInteger)page +{ + return [[_private->pageRects objectAtIndex:page - 1] rectValue]; +} + +- (void)drawPageBorderWithSize:(NSSize)borderSize +{ + ASSERT(NSEqualSizes(borderSize, [[[NSPrintOperation currentOperation] printInfo] paperSize])); + [[self _webView] _drawHeaderAndFooter]; +} + +- (void)beginDocument +{ + @try { + // From now on we'll get a chance to call _endPrintMode in either beginDocument or + // endDocument, so we can cancel the "just in case" pending call. + [NSObject cancelPreviousPerformRequestsWithTarget:self + selector:@selector(_delayedEndPrintMode:) + object:[NSPrintOperation currentOperation]]; + [super beginDocument]; + } @catch (NSException *localException) { + // Exception during [super beginDocument] means that endDocument will not get called, + // so we need to clean up our "print mode" here. + [self _endPrintModeAndRestoreWindowAutodisplay]; + } +} + +- (void)endDocument +{ + [super endDocument]; + // Note sadly at this point [NSGraphicsContext currentContextDrawingToScreen] is still NO + [self _endPrintModeAndRestoreWindowAutodisplay]; +} + +- (void)keyDown:(NSEvent *)event +{ + // There's a chance that responding to this event will run a nested event loop, and + // fetching a new event might release the old one. Retaining and then autoreleasing + // the current event prevents that from causing a problem inside WebKit or AppKit code. + [[event retain] autorelease]; + + RetainPtr<WebHTMLView> selfProtector = self; + BOOL eventWasSentToWebCore = (_private->keyDownEvent == event); + + BOOL callSuper = NO; + + [_private->keyDownEvent release]; + _private->keyDownEvent = [event retain]; + + BOOL completionPopupWasOpen = _private->completionController && [_private->completionController popupWindowIsOpen]; + Frame* coreFrame = core([self _frame]); + if (!eventWasSentToWebCore && coreFrame && coreFrame->eventHandler()->keyEvent(event)) { + // WebCore processed a key event, bail on any preexisting complete: UI + if (completionPopupWasOpen) + [_private->completionController endRevertingChange:YES moveLeft:NO]; + } else if (!_private->completionController || ![_private->completionController filterKeyDown:event]) { + // Not consumed by complete: popup window + [_private->completionController endRevertingChange:YES moveLeft:NO]; + callSuper = YES; + } + if (callSuper) + [super keyDown:event]; + else + [NSCursor setHiddenUntilMouseMoves:YES]; +} + +- (void)keyUp:(NSEvent *)event +{ + // There's a chance that responding to this event will run a nested event loop, and + // fetching a new event might release the old one. Retaining and then autoreleasing + // the current event prevents that from causing a problem inside WebKit or AppKit code. + [[event retain] autorelease]; + + BOOL eventWasSentToWebCore = (_private->keyDownEvent == event); + + RetainPtr<WebHTMLView> selfProtector = self; + Frame* coreFrame = core([self _frame]); + if (coreFrame && !eventWasSentToWebCore) + coreFrame->eventHandler()->keyEvent(event); + else + [super keyUp:event]; +} + +- (void)flagsChanged:(NSEvent *)event +{ + // There's a chance that responding to this event will run a nested event loop, and + // fetching a new event might release the old one. Retaining and then autoreleasing + // the current event prevents that from causing a problem inside WebKit or AppKit code. + [[event retain] autorelease]; + + Frame* coreFrame = core([self _frame]); + if (coreFrame) + coreFrame->eventHandler()->capsLockStateMayHaveChanged(); + + RetainPtr<WebHTMLView> selfProtector = self; + + unsigned short keyCode = [event keyCode]; + + //Don't make an event from the num lock and function keys + if (coreFrame && keyCode != 0 && keyCode != 10 && keyCode != 63) { + coreFrame->eventHandler()->keyEvent(PlatformKeyboardEvent(event)); + return; + } + + [super flagsChanged:event]; +} + +- (id)accessibilityAttributeValue:(NSString*)attributeName +{ + if ([attributeName isEqualToString: NSAccessibilityChildrenAttribute]) { + id accTree = [[self _frame] accessibilityRoot]; + if (accTree) + return [NSArray arrayWithObject:accTree]; + return nil; + } + return [super accessibilityAttributeValue:attributeName]; +} + +- (id)accessibilityFocusedUIElement +{ + id accTree = [[self _frame] accessibilityRoot]; + if (accTree) + return [accTree accessibilityFocusedUIElement]; + return self; +} + +- (id)accessibilityHitTest:(NSPoint)point +{ + id accTree = [[self _frame] accessibilityRoot]; + if (accTree) { + NSPoint windowCoord = [[self window] convertScreenToBase:point]; + return [accTree accessibilityHitTest:[self convertPoint:windowCoord fromView:nil]]; + } + return self; +} + +- (id)_accessibilityParentForSubview:(NSView *)subview +{ + id accTree = [[self _frame] accessibilityRoot]; + if (!accTree) + return self; + id parent = [accTree _accessibilityParentForSubview:subview]; + if (!parent) + return self; + return parent; +} + +- (void)centerSelectionInVisibleArea:(id)sender +{ + COMMAND_PROLOGUE + + if (Frame* coreFrame = core([self _frame])) + coreFrame->selection()->revealSelection(ScrollAlignment::alignCenterAlways); +} + +- (NSData *)_selectionStartFontAttributesAsRTF +{ + Frame* coreFrame = core([self _frame]); + NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"x" + attributes:coreFrame ? coreFrame->editor()->fontAttributesForSelectionStart() : nil]; + NSData *data = [string RTFFromRange:NSMakeRange(0, [string length]) documentAttributes:nil]; + [string release]; + return data; +} + +- (NSDictionary *)_fontAttributesFromFontPasteboard +{ + NSPasteboard *fontPasteboard = [NSPasteboard pasteboardWithName:NSFontPboard]; + if (fontPasteboard == nil) + return nil; + NSData *data = [fontPasteboard dataForType:NSFontPboardType]; + if (data == nil || [data length] == 0) + return nil; + // NSTextView does something more efficient by parsing the attributes only, but that's not available in API. + NSAttributedString *string = [[[NSAttributedString alloc] initWithRTF:data documentAttributes:NULL] autorelease]; + if (string == nil || [string length] == 0) + return nil; + return [string fontAttributesInRange:NSMakeRange(0, 1)]; +} + +- (DOMCSSStyleDeclaration *)_emptyStyle +{ + return [[[self _frame] DOMDocument] createCSSStyleDeclaration]; +} + +- (NSString *)_colorAsString:(NSColor *)color +{ + NSColor *rgbColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace]; + // FIXME: If color is non-nil and rgbColor is nil, that means we got some kind + // of fancy color that can't be converted to RGB. Changing that to "transparent" + // might not be great, but it's probably OK. + if (rgbColor == nil) + return @"transparent"; + float r = [rgbColor redComponent]; + float g = [rgbColor greenComponent]; + float b = [rgbColor blueComponent]; + float a = [rgbColor alphaComponent]; + if (a == 0) + return @"transparent"; + if (r == 0 && g == 0 && b == 0 && a == 1) + return @"black"; + if (r == 1 && g == 1 && b == 1 && a == 1) + return @"white"; + // FIXME: Lots more named colors. Maybe we could use the table in WebCore? + if (a == 1) + return [NSString stringWithFormat:@"rgb(%.0f,%.0f,%.0f)", r * 255, g * 255, b * 255]; + return [NSString stringWithFormat:@"rgba(%.0f,%.0f,%.0f,%f)", r * 255, g * 255, b * 255, a]; +} + +- (NSString *)_shadowAsString:(NSShadow *)shadow +{ + if (shadow == nil) + return @"none"; + NSSize offset = [shadow shadowOffset]; + float blurRadius = [shadow shadowBlurRadius]; + if (offset.width == 0 && offset.height == 0 && blurRadius == 0) + return @"none"; + NSColor *color = [shadow shadowColor]; + if (color == nil) + return @"none"; + // FIXME: Handle non-integral values here? + if (blurRadius == 0) + return [NSString stringWithFormat:@"%@ %.0fpx %.0fpx", [self _colorAsString:color], offset.width, offset.height]; + return [NSString stringWithFormat:@"%@ %.0fpx %.0fpx %.0fpx", [self _colorAsString:color], offset.width, offset.height, blurRadius]; +} + +- (DOMCSSStyleDeclaration *)_styleFromFontAttributes:(NSDictionary *)dictionary +{ + DOMCSSStyleDeclaration *style = [self _emptyStyle]; + + NSColor *color = [dictionary objectForKey:NSBackgroundColorAttributeName]; + [style setBackgroundColor:[self _colorAsString:color]]; + + NSFont *font = [dictionary objectForKey:NSFontAttributeName]; + if (!font) { + [style setFontFamily:@"Helvetica"]; + [style setFontSize:@"12px"]; + [style setFontWeight:@"normal"]; + [style setFontStyle:@"normal"]; + } else { + NSFontManager *fm = [NSFontManager sharedFontManager]; + // FIXME: Need more sophisticated escaping code if we want to handle family names + // with characters like single quote or backslash in their names. + [style setFontFamily:[NSString stringWithFormat:@"'%@'", [font familyName]]]; + [style setFontSize:[NSString stringWithFormat:@"%0.fpx", [font pointSize]]]; + // FIXME: Map to the entire range of CSS weight values. + if ([fm weightOfFont:font] >= MIN_BOLD_WEIGHT) + [style setFontWeight:@"bold"]; + else + [style setFontWeight:@"normal"]; + if ([fm traitsOfFont:font] & NSItalicFontMask) + [style setFontStyle:@"italic"]; + else + [style setFontStyle:@"normal"]; + } + + color = [dictionary objectForKey:NSForegroundColorAttributeName]; + [style setColor:color ? [self _colorAsString:color] : (NSString *)@"black"]; + + NSShadow *shadow = [dictionary objectForKey:NSShadowAttributeName]; + [style setTextShadow:[self _shadowAsString:shadow]]; + + int strikethroughInt = [[dictionary objectForKey:NSStrikethroughStyleAttributeName] intValue]; + + int superscriptInt = [[dictionary objectForKey:NSSuperscriptAttributeName] intValue]; + if (superscriptInt > 0) + [style setVerticalAlign:@"super"]; + else if (superscriptInt < 0) + [style setVerticalAlign:@"sub"]; + else + [style setVerticalAlign:@"baseline"]; + int underlineInt = [[dictionary objectForKey:NSUnderlineStyleAttributeName] intValue]; + // FIXME: Underline wins here if we have both (see bug 3790443). + if (strikethroughInt == NSUnderlineStyleNone && underlineInt == NSUnderlineStyleNone) + [style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""]; + else if (underlineInt == NSUnderlineStyleNone) + [style setProperty:@"-khtml-text-decorations-in-effect" value:@"line-through" priority:@""]; + else + [style setProperty:@"-khtml-text-decorations-in-effect" value:@"underline" priority:@""]; + + return style; +} + +- (void)_applyStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction +{ + if (Frame* coreFrame = core([self _frame])) + coreFrame->editor()->applyStyleToSelection(core(style), undoAction); +} + +- (void)_applyParagraphStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(EditAction)undoAction +{ + if (Frame* coreFrame = core([self _frame])) + coreFrame->editor()->applyParagraphStyleToSelection(core(style), undoAction); +} + +- (BOOL)_handleStyleKeyEquivalent:(NSEvent *)event +{ + ASSERT([self _webView]); + if (![[[self _webView] preferences] respectStandardStyleKeyEquivalents]) + return NO; + + if (![self _canEdit]) + return NO; + + if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) != NSCommandKeyMask) + return NO; + + NSString *string = [event characters]; + if ([string caseInsensitiveCompare:@"b"] == NSOrderedSame) { + [self executeCoreCommandByName:"ToggleBold"]; + return YES; + } + if ([string caseInsensitiveCompare:@"i"] == NSOrderedSame) { + [self executeCoreCommandByName:"ToggleItalic"]; + return YES; + } + + return NO; +} + +- (BOOL)performKeyEquivalent:(NSEvent *)event +{ + // There's a chance that responding to this event will run a nested event loop, and + // fetching a new event might release the old one. Retaining and then autoreleasing + // the current event prevents that from causing a problem inside WebKit or AppKit code. + [[event retain] autorelease]; + + BOOL eventWasSentToWebCore = (_private->keyDownEvent == event); + BOOL ret = NO; + + [_private->keyDownEvent release]; + _private->keyDownEvent = [event retain]; + + [self retain]; + + // Pass command-key combos through WebCore if there is a key binding available for + // this event. This lets web pages have a crack at intercepting command-modified keypresses. + // But don't do it if we have already handled the event. + // Pressing Esc results in a fake event being sent - don't pass it to WebCore. + if (!eventWasSentToWebCore && event == [NSApp currentEvent] && self == [[self window] firstResponder]) + if (Frame* frame = core([self _frame])) + ret = frame->eventHandler()->keyEvent(event); + + if (!ret) + ret = [self _handleStyleKeyEquivalent:event] || [super performKeyEquivalent:event]; + + [self release]; + + return ret; +} + +- (void)copyFont:(id)sender +{ + COMMAND_PROLOGUE + + // Put RTF with font attributes on the pasteboard. + // Maybe later we should add a pasteboard type that contains CSS text for "native" copy and paste font. + NSPasteboard *fontPasteboard = [NSPasteboard pasteboardWithName:NSFontPboard]; + [fontPasteboard declareTypes:[NSArray arrayWithObject:NSFontPboardType] owner:nil]; + [fontPasteboard setData:[self _selectionStartFontAttributesAsRTF] forType:NSFontPboardType]; +} + +- (void)pasteFont:(id)sender +{ + COMMAND_PROLOGUE + + // Read RTF with font attributes from the pasteboard. + // Maybe later we should add a pasteboard type that contains CSS text for "native" copy and paste font. + [self _applyStyleToSelection:[self _styleFromFontAttributes:[self _fontAttributesFromFontPasteboard]] withUndoAction:EditActionPasteFont]; +} + +- (void)pasteAsRichText:(id)sender +{ + COMMAND_PROLOGUE + + // Since rich text always beats plain text when both are on the pasteboard, it's not + // clear how this is different from plain old paste. + [self _pasteWithPasteboard:[NSPasteboard generalPasteboard] allowPlainText:NO]; +} + +- (NSFont *)_originalFontA +{ + return [[NSFontManager sharedFontManager] fontWithFamily:@"Helvetica" traits:0 weight:STANDARD_WEIGHT size:10.0f]; +} + +- (NSFont *)_originalFontB +{ + return [[NSFontManager sharedFontManager] fontWithFamily:@"Times" traits:NSFontItalicTrait weight:STANDARD_BOLD_WEIGHT size:12.0f]; +} + +- (void)_addToStyle:(DOMCSSStyleDeclaration *)style fontA:(NSFont *)a fontB:(NSFont *)b +{ + // Since there's no way to directly ask NSFontManager what style change it's going to do + // we instead pass two "specimen" fonts to it and let it change them. We then deduce what + // style change it was doing by looking at what happened to each of the two fonts. + // So if it was making the text bold, both fonts will be bold after the fact. + + if (a == nil || b == nil) + return; + + NSFontManager *fm = [NSFontManager sharedFontManager]; + + NSFont *oa = [self _originalFontA]; + + NSString *aFamilyName = [a familyName]; + NSString *bFamilyName = [b familyName]; + + int aPointSize = (int)[a pointSize]; + int bPointSize = (int)[b pointSize]; + + int aWeight = [fm weightOfFont:a]; + int bWeight = [fm weightOfFont:b]; + + BOOL aIsItalic = ([fm traitsOfFont:a] & NSItalicFontMask) != 0; + BOOL bIsItalic = ([fm traitsOfFont:b] & NSItalicFontMask) != 0; + + BOOL aIsBold = aWeight > MIN_BOLD_WEIGHT; + + if ([aFamilyName isEqualToString:bFamilyName]) { + NSString *familyNameForCSS = aFamilyName; + + // The family name may not be specific enough to get us the font specified. + // In some cases, the only way to get exactly what we are looking for is to use + // the Postscript name. + + // Find the font the same way the rendering code would later if it encountered this CSS. + NSFontTraitMask traits = aIsItalic ? NSFontItalicTrait : 0; + int weight = aIsBold ? STANDARD_BOLD_WEIGHT : STANDARD_WEIGHT; + NSFont *foundFont = [WebFontCache fontWithFamily:aFamilyName traits:traits weight:weight size:aPointSize]; + + // If we don't find a font with the same Postscript name, then we'll have to use the + // Postscript name to make the CSS specific enough. + if (![[foundFont fontName] isEqualToString:[a fontName]]) + familyNameForCSS = [a fontName]; + + // FIXME: Need more sophisticated escaping code if we want to handle family names + // with characters like single quote or backslash in their names. + [style setFontFamily:[NSString stringWithFormat:@"'%@'", familyNameForCSS]]; + } + + int soa = (int)[oa pointSize]; + if (aPointSize == bPointSize) + [style setFontSize:[NSString stringWithFormat:@"%dpx", aPointSize]]; + else if (aPointSize < soa) + [style _setFontSizeDelta:@"-1px"]; + else if (aPointSize > soa) + [style _setFontSizeDelta:@"1px"]; + + // FIXME: Map to the entire range of CSS weight values. + if (aWeight == bWeight) + [style setFontWeight:aIsBold ? @"bold" : @"normal"]; + + if (aIsItalic == bIsItalic) + [style setFontStyle:aIsItalic ? @"italic" : @"normal"]; +} + +- (DOMCSSStyleDeclaration *)_styleFromFontManagerOperation +{ + DOMCSSStyleDeclaration *style = [self _emptyStyle]; + + NSFontManager *fm = [NSFontManager sharedFontManager]; + + NSFont *oa = [self _originalFontA]; + NSFont *ob = [self _originalFontB]; + [self _addToStyle:style fontA:[fm convertFont:oa] fontB:[fm convertFont:ob]]; + + return style; +} + +- (void)changeFont:(id)sender +{ + COMMAND_PROLOGUE + + [self _applyStyleToSelection:[self _styleFromFontManagerOperation] withUndoAction:EditActionSetFont]; +} + +- (DOMCSSStyleDeclaration *)_styleForAttributeChange:(id)sender +{ + DOMCSSStyleDeclaration *style = [self _emptyStyle]; + + NSShadow *shadow = [[NSShadow alloc] init]; + [shadow setShadowOffset:NSMakeSize(1, 1)]; + + NSDictionary *oa = [NSDictionary dictionaryWithObjectsAndKeys: + [self _originalFontA], NSFontAttributeName, + nil]; + NSDictionary *ob = [NSDictionary dictionaryWithObjectsAndKeys: + [NSColor blackColor], NSBackgroundColorAttributeName, + [self _originalFontB], NSFontAttributeName, + [NSColor whiteColor], NSForegroundColorAttributeName, + shadow, NSShadowAttributeName, + [NSNumber numberWithInt:NSUnderlineStyleSingle], NSStrikethroughStyleAttributeName, + [NSNumber numberWithInt:1], NSSuperscriptAttributeName, + [NSNumber numberWithInt:NSUnderlineStyleSingle], NSUnderlineStyleAttributeName, + nil]; + + [shadow release]; + +#if 0 + +NSObliquenessAttributeName /* float; skew to be applied to glyphs, default 0: no skew */ + // font-style, but that is just an on-off switch + +NSExpansionAttributeName /* float; log of expansion factor to be applied to glyphs, default 0: no expansion */ + // font-stretch? + +NSKernAttributeName /* float, amount to modify default kerning, if 0, kerning off */ + // letter-spacing? probably not good enough + +NSUnderlineColorAttributeName /* NSColor, default nil: same as foreground color */ +NSStrikethroughColorAttributeName /* NSColor, default nil: same as foreground color */ + // text-decoration-color? + +NSLigatureAttributeName /* int, default 1: default ligatures, 0: no ligatures, 2: all ligatures */ +NSBaselineOffsetAttributeName /* float, in points; offset from baseline, default 0 */ +NSStrokeWidthAttributeName /* float, in percent of font point size, default 0: no stroke; positive for stroke alone, negative for stroke and fill (a typical value for outlined text would be 3.0) */ +NSStrokeColorAttributeName /* NSColor, default nil: same as foreground color */ + // need extensions? + +#endif + + NSDictionary *a = [sender convertAttributes:oa]; + NSDictionary *b = [sender convertAttributes:ob]; + + NSColor *ca = [a objectForKey:NSBackgroundColorAttributeName]; + NSColor *cb = [b objectForKey:NSBackgroundColorAttributeName]; + if (ca == cb) { + [style setBackgroundColor:[self _colorAsString:ca]]; + } + + [self _addToStyle:style fontA:[a objectForKey:NSFontAttributeName] fontB:[b objectForKey:NSFontAttributeName]]; + + ca = [a objectForKey:NSForegroundColorAttributeName]; + cb = [b objectForKey:NSForegroundColorAttributeName]; + if (ca == cb) { + [style setColor:[self _colorAsString:ca]]; + } + + NSShadow *sha = [a objectForKey:NSShadowAttributeName]; + if (sha) + [style setTextShadow:[self _shadowAsString:sha]]; + else if ([b objectForKey:NSShadowAttributeName] == nil) + [style setTextShadow:@"none"]; + + int sa = [[a objectForKey:NSStrikethroughStyleAttributeName] intValue]; + int sb = [[b objectForKey:NSStrikethroughStyleAttributeName] intValue]; + if (sa == sb) { + if (sa == NSUnderlineStyleNone) + [style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""]; + // we really mean "no line-through" rather than "none" + else + [style setProperty:@"-khtml-text-decorations-in-effect" value:@"line-through" priority:@""]; + // we really mean "add line-through" rather than "line-through" + } + + sa = [[a objectForKey:NSSuperscriptAttributeName] intValue]; + sb = [[b objectForKey:NSSuperscriptAttributeName] intValue]; + if (sa == sb) { + if (sa > 0) + [style setVerticalAlign:@"super"]; + else if (sa < 0) + [style setVerticalAlign:@"sub"]; + else + [style setVerticalAlign:@"baseline"]; + } + + int ua = [[a objectForKey:NSUnderlineStyleAttributeName] intValue]; + int ub = [[b objectForKey:NSUnderlineStyleAttributeName] intValue]; + if (ua == ub) { + if (ua == NSUnderlineStyleNone) + [style setProperty:@"-khtml-text-decorations-in-effect" value:@"none" priority:@""]; + // we really mean "no underline" rather than "none" + else + [style setProperty:@"-khtml-text-decorations-in-effect" value:@"underline" priority:@""]; + // we really mean "add underline" rather than "underline" + } + + return style; +} + +- (void)changeAttributes:(id)sender +{ + COMMAND_PROLOGUE + + [self _applyStyleToSelection:[self _styleForAttributeChange:sender] withUndoAction:EditActionChangeAttributes]; +} + +- (DOMCSSStyleDeclaration *)_styleFromColorPanelWithSelector:(SEL)selector +{ + DOMCSSStyleDeclaration *style = [self _emptyStyle]; + + ASSERT([style respondsToSelector:selector]); + [style performSelector:selector withObject:[self _colorAsString:[[NSColorPanel sharedColorPanel] color]]]; + + return style; +} + +- (EditAction)_undoActionFromColorPanelWithSelector:(SEL)selector +{ + if (selector == @selector(setBackgroundColor:)) + return EditActionSetBackgroundColor; + return EditActionSetColor; +} + +- (void)_changeCSSColorUsingSelector:(SEL)selector inRange:(DOMRange *)range +{ + DOMCSSStyleDeclaration *style = [self _styleFromColorPanelWithSelector:selector]; + WebView *webView = [self _webView]; + if ([[webView _editingDelegateForwarder] webView:webView shouldApplyStyle:style toElementsInDOMRange:range]) + if (Frame* coreFrame = core([self _frame])) + coreFrame->editor()->applyStyle(core(style), [self _undoActionFromColorPanelWithSelector:selector]); +} + +- (void)changeDocumentBackgroundColor:(id)sender +{ + COMMAND_PROLOGUE + + // Mimicking NSTextView, this method sets the background color for the + // entire document. There is no NSTextView API for setting the background + // color on the selected range only. Note that this method is currently + // never called from the UI (see comment in changeColor:). + // FIXME: this actually has no effect when called, probably due to 3654850. _documentRange seems + // to do the right thing because it works in startSpeaking:, and I know setBackgroundColor: does the + // right thing because I tested it with [self _selectedRange]. + // FIXME: This won't actually apply the style to the entire range here, because it ends up calling + // [frame _applyStyle:], which operates on the current selection. To make this work right, we'll + // need to save off the selection, temporarily set it to the entire range, make the change, then + // restore the old selection. + [self _changeCSSColorUsingSelector:@selector(setBackgroundColor:) inRange:[self _documentRange]]; +} + +- (void)changeColor:(id)sender +{ + COMMAND_PROLOGUE + + // FIXME: in NSTextView, this method calls changeDocumentBackgroundColor: when a + // private call has earlier been made by [NSFontFontEffectsBox changeColor:], see 3674493. + // AppKit will have to be revised to allow this to work with anything that isn't an + // NSTextView. However, this might not be required for Tiger, since the background-color + // changing box in the font panel doesn't work in Mail (3674481), though it does in TextEdit. + [self _applyStyleToSelection:[self _styleFromColorPanelWithSelector:@selector(setColor:)] + withUndoAction:EditActionSetColor]; +} + +- (void)_changeWordCaseWithSelector:(SEL)selector +{ + if (![self _canEdit]) + return; + + WebFrame *frame = [self _frame]; + [self selectWord:nil]; + NSString *word = [[frame _selectedString] performSelector:selector]; + // FIXME: Does this need a different action context other than "typed"? + if ([self _shouldReplaceSelectionWithText:word givenAction:WebViewInsertActionTyped]) + [frame _replaceSelectionWithText:word selectReplacement:NO smartReplace:NO]; +} + +- (void)uppercaseWord:(id)sender +{ + COMMAND_PROLOGUE + + [self _changeWordCaseWithSelector:@selector(uppercaseString)]; +} + +- (void)lowercaseWord:(id)sender +{ + COMMAND_PROLOGUE + + [self _changeWordCaseWithSelector:@selector(lowercaseString)]; +} + +- (void)capitalizeWord:(id)sender +{ + COMMAND_PROLOGUE + + [self _changeWordCaseWithSelector:@selector(capitalizedString)]; +} + +- (void)complete:(id)sender +{ + COMMAND_PROLOGUE + + if (![self _canEdit]) + return; + if (!_private->completionController) + _private->completionController = [[WebTextCompletionController alloc] initWithWebView:[self _webView] HTMLView:self]; + [_private->completionController doCompletion]; +} + +- (void)checkSpelling:(id)sender +{ + COMMAND_PROLOGUE + + if (Frame* coreFrame = core([self _frame])) + coreFrame->editor()->advanceToNextMisspelling(); +} + +- (void)showGuessPanel:(id)sender +{ + COMMAND_PROLOGUE + + NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker]; + if (!checker) { + LOG_ERROR("No NSSpellChecker"); + return; + } + + NSPanel *spellingPanel = [checker spellingPanel]; +#ifndef BUILDING_ON_TIGER + // Post-Tiger, this menu item is a show/hide toggle, to match AppKit. Leave Tiger behavior alone + // to match rest of OS X. + if ([spellingPanel isVisible]) { + [spellingPanel orderOut:sender]; + return; + } +#endif + + if (Frame* coreFrame = core([self _frame])) + coreFrame->editor()->advanceToNextMisspelling(true); + [spellingPanel orderFront:sender]; +} + +- (void)_changeSpellingToWord:(NSString *)newWord +{ + if (![self _canEdit]) + return; + + // Don't correct to empty string. (AppKit checked this, we might as well too.) + if (![NSSpellChecker sharedSpellChecker]) { + LOG_ERROR("No NSSpellChecker"); + return; + } + + if ([newWord isEqualToString:@""]) + return; + + if ([self _shouldReplaceSelectionWithText:newWord givenAction:WebViewInsertActionPasted]) + [[self _frame] _replaceSelectionWithText:newWord selectReplacement:YES smartReplace:NO]; +} + +- (void)changeSpelling:(id)sender +{ + COMMAND_PROLOGUE + + [self _changeSpellingToWord:[[sender selectedCell] stringValue]]; +} + +- (void)performFindPanelAction:(id)sender +{ + COMMAND_PROLOGUE + + // Implementing this will probably require copying all of NSFindPanel.h and .m. + // We need *almost* the same thing as AppKit, but not quite. + LOG_ERROR("unimplemented"); +} + +- (void)startSpeaking:(id)sender +{ + COMMAND_PROLOGUE + + WebFrame *frame = [self _frame]; + DOMRange *range = [self _selectedRange]; + if (!range || [range collapsed]) + range = [self _documentRange]; + [NSApp speakString:[frame _stringForRange:range]]; +} + +- (void)stopSpeaking:(id)sender +{ + COMMAND_PROLOGUE + + [NSApp stopSpeaking:sender]; +} + +- (void)toggleBaseWritingDirection:(id)sender +{ + COMMAND_PROLOGUE + + if (![self _canEdit]) + return; + + Frame* coreFrame = core([self _frame]); + if (!coreFrame) + return; + + WritingDirection direction = RightToLeftWritingDirection; + switch (coreFrame->editor()->baseWritingDirectionForSelectionStart()) { + case NSWritingDirectionLeftToRight: + break; + case NSWritingDirectionRightToLeft: + direction = LeftToRightWritingDirection; + break; + // The writingDirectionForSelectionStart method will never return "natural". It + // will always return a concrete direction. So, keep the compiler happy, and assert not reached. + case NSWritingDirectionNatural: + ASSERT_NOT_REACHED(); + break; + } + + if (Frame* coreFrame = core([self _frame])) + coreFrame->editor()->setBaseWritingDirection(direction); +} + +- (void)changeBaseWritingDirection:(id)sender +{ + COMMAND_PROLOGUE + + if (![self _canEdit]) + return; + + NSWritingDirection writingDirection = static_cast<NSWritingDirection>([sender tag]); + + // We disable the menu item that performs this action because we can't implement + // NSWritingDirectionNatural's behavior using CSS. + ASSERT(writingDirection != NSWritingDirectionNatural); + + if (Frame* coreFrame = core([self _frame])) + coreFrame->editor()->setBaseWritingDirection(writingDirection == NSWritingDirectionLeftToRight ? LeftToRightWritingDirection : RightToLeftWritingDirection); +} + +static BOOL writingDirectionKeyBindingsEnabled() +{ +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + return YES; +#else + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + return [defaults boolForKey:@"NSAllowsBaseWritingDirectionKeyBindings"] || [defaults boolForKey:@"AppleTextDirection"]; +#endif +} + +- (void)_changeBaseWritingDirectionTo:(NSWritingDirection)direction +{ + if (![self _canEdit]) + return; + + static BOOL bindingsEnabled = writingDirectionKeyBindingsEnabled(); + + if (!bindingsEnabled) { + NSBeep(); + return; + } + + if (Frame* coreFrame = core([self _frame])) + coreFrame->editor()->setBaseWritingDirection(direction == NSWritingDirectionLeftToRight ? LeftToRightWritingDirection : RightToLeftWritingDirection); +} + +- (void)makeBaseWritingDirectionLeftToRight:(id)sender +{ + COMMAND_PROLOGUE + + [self _changeBaseWritingDirectionTo:NSWritingDirectionLeftToRight]; +} + +- (void)makeBaseWritingDirectionRightToLeft:(id)sender +{ + COMMAND_PROLOGUE + + [self _changeBaseWritingDirectionTo:NSWritingDirectionRightToLeft]; +} + +#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) +- (void)changeBaseWritingDirectionToLTR:(id)sender +{ + [self makeBaseWritingDirectionLeftToRight:sender]; +} + +- (void)changeBaseWritingDirectionToRTL:(id)sender +{ + [self makeBaseWritingDirectionRightToLeft:sender]; +} +#endif + +- (void)makeBaseWritingDirectionNatural:(id)sender +{ + LOG_ERROR("Sent from %@.", sender); +} + +#if 0 + +// CSS does not have a way to specify an outline font, which may make this difficult to implement. +// Maybe a special case of text-shadow? +- (void)outline:(id)sender; + +// This is part of table support, which may be in NSTextView for Tiger. +// It's probably simple to do the equivalent thing for WebKit. +- (void)insertTable:(id)sender; + +// This could be important. +- (void)toggleTraditionalCharacterShape:(id)sender; + +// I'm not sure what the equivalents of these in the web world are. +- (void)insertLineSeparator:(id)sender; +- (void)insertPageBreak:(id)sender; + +// These methods are not implemented in NSTextView yet at the time of this writing. +- (void)changeCaseOfLetter:(id)sender; +- (void)transposeWords:(id)sender; + +#endif + +#ifndef BUILDING_ON_TIGER + +// Override this so that AppKit will send us arrow keys as key down events so we can +// support them via the key bindings mechanism. +- (BOOL)_wantsKeyDownForEvent:(NSEvent *)event +{ + bool haveWebCoreFrame = core([self _frame]); + + // If we have a frame, our keyDown method will handle key bindings after sending + // the event through the DOM, so ask AppKit not to do its early special key binding + // mapping. If we don't have a frame, just let things work the normal way without + // a keyDown. + return haveWebCoreFrame; +} + +#else + +// Super-hack alert. +// All this code accomplishes the same thing as the _wantsKeyDownForEvent method above. + +// Returns a selector only if called while: +// 1) first responder is self +// 2) handling a key down event +// 3) not yet inside keyDown: method +// 4) key is an arrow key +// The selector is the one that gets sent by -[NSWindow _processKeyboardUIKey] for this key. +- (SEL)_arrowKeyDownEventSelectorIfPreprocessing +{ + NSWindow *w = [self window]; + if ([w firstResponder] != self) + return NULL; + NSEvent *e = [w currentEvent]; + if ([e type] != NSKeyDown) + return NULL; + if (e == _private->keyDownEvent) + return NULL; + NSString *s = [e charactersIgnoringModifiers]; + if ([s length] == 0) + return NULL; + switch ([s characterAtIndex:0]) { + case NSDownArrowFunctionKey: + return @selector(moveDown:); + case NSLeftArrowFunctionKey: + return @selector(moveLeft:); + case NSRightArrowFunctionKey: + return @selector(moveRight:); + case NSUpArrowFunctionKey: + return @selector(moveUp:); + default: + return NULL; + } +} + +// Returns NO instead of YES if called on the selector that the +// _arrowKeyDownEventSelectorIfPreprocessing method returns. +// This should only happen inside -[NSWindow _processKeyboardUIKey], +// and together with the change below should cause that method +// to return NO rather than handling the key. +// Also set a 1-shot flag for the nextResponder check below. +- (BOOL)respondsToSelector:(SEL)selector +{ + if (![super respondsToSelector:selector]) + return NO; + SEL arrowKeySelector = [self _arrowKeyDownEventSelectorIfPreprocessing]; + if (selector != arrowKeySelector) + return YES; + _private->nextResponderDisabledOnce = YES; + return NO; +} + +// Returns nil instead of the next responder if called when the +// one-shot flag is set, and _arrowKeyDownEventSelectorIfPreprocessing +// returns something other than NULL. This should only happen inside +// -[NSWindow _processKeyboardUIKey] and together with the change above +// should cause that method to return NO rather than handling the key. +- (NSResponder *)nextResponder +{ + BOOL disabled = _private->nextResponderDisabledOnce; + _private->nextResponderDisabledOnce = NO; + if (disabled && [self _arrowKeyDownEventSelectorIfPreprocessing] != NULL) + return nil; + return [super nextResponder]; +} + +#endif + +- (void)_updateControlTints +{ + Frame* frame = core([self _frame]); + if (!frame) + return; + FrameView* view = frame->view(); + if (!view) + return; + view->updateControlTints(); +} + +// Despite its name, this is called at different times than windowDidBecomeKey is. +// It takes into account all the other factors that determine when NSCell draws +// with different tints, so it's the right call to use for control tints. We'd prefer +// to do this with API. <rdar://problem/5136760> +- (void)_windowChangedKeyState +{ + if (pthread_main_np()) + [self _updateControlTints]; + else + [self performSelectorOnMainThread:@selector(_updateControlTints) withObject:nil waitUntilDone:NO]; + + [super _windowChangedKeyState]; +} + +- (void)otherMouseDown:(NSEvent *)event +{ + if ([event buttonNumber] == 2) + [self mouseDown:event]; + else + [super otherMouseDown:event]; +} + +- (void)otherMouseDragged:(NSEvent *)event +{ + if ([event buttonNumber] == 2) + [self mouseDragged:event]; + else + [super otherMouseDragged:event]; +} + +- (void)otherMouseUp:(NSEvent *)event +{ + if ([event buttonNumber] == 2) + [self mouseUp:event]; + else + [super otherMouseUp:event]; +} + +@end + +@implementation WebHTMLView (WebInternal) + +- (void)_selectionChanged +{ + [self _updateSelectionForInputManager]; + [self _updateFontPanel]; + if (Frame* coreFrame = core([self _frame])) + coreFrame->editor()->setStartNewKillRingSequence(true); +} + +- (void)_updateFontPanel +{ + // FIXME: NSTextView bails out if becoming or resigning first responder, for which it has ivar flags. Not + // sure if we need to do something similar. + + if (![self _canEdit]) + return; + + NSWindow *window = [self window]; + // FIXME: is this first-responder check correct? What happens if a subframe is editable and is first responder? + if (![window isKeyWindow] || [window firstResponder] != self) + return; + + bool multipleFonts = false; + NSFont *font = nil; + if (Frame* coreFrame = core([self _frame])) { + if (const SimpleFontData* fd = coreFrame->editor()->fontForSelection(multipleFonts)) + font = fd->getNSFont(); + } + + // FIXME: for now, return a bogus font that distinguishes the empty selection from the non-empty + // selection. We should be able to remove this once the rest of this code works properly. + if (font == nil) + font = [self _hasSelection] ? [NSFont menuFontOfSize:23] : [NSFont toolTipsFontOfSize:17]; + ASSERT(font != nil); + + [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:multipleFonts]; + + // FIXME: we don't keep track of selected attributes, or set them on the font panel. This + // appears to have no effect on the UI. E.g., underlined text in Mail or TextEdit is + // not reflected in the font panel. Maybe someday this will change. +} + +- (BOOL)_canSmartCopyOrDelete +{ + if (![[self _webView] smartInsertDeleteEnabled]) + return NO; + Frame* coreFrame = core([self _frame]); + return coreFrame && coreFrame->selection()->granularity() == WordGranularity; +} + +- (NSEvent *)_mouseDownEvent +{ + return _private->mouseDownEvent; +} + +- (id<WebHTMLHighlighter>)_highlighterForType:(NSString*)type +{ + return [_private->highlighters objectForKey:type]; +} + +- (WebFrame *)_frame +{ + return [_private->dataSource webFrame]; +} + +- (void)closeIfNotCurrentView +{ + if ([[[self _frame] frameView] documentView] != self) + [self close]; +} + +- (DOMDocumentFragment*)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard +{ + return [self _documentFragmentFromPasteboard:pasteboard inContext:nil allowPlainText:NO]; +} + +#ifndef BUILDING_ON_TIGER + +- (BOOL)isGrammarCheckingEnabled +{ + // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must implement the method here because + // the AppKit code checks the first responder. + return [[self _webView] isGrammarCheckingEnabled]; +} + +- (void)setGrammarCheckingEnabled:(BOOL)flag +{ + // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must implement the method here because + // the AppKit code checks the first responder. + [[self _webView] setGrammarCheckingEnabled:flag]; +} + +- (void)toggleGrammarChecking:(id)sender +{ + // FIXME 4799134: WebView is the bottleneck for this grammar-checking logic, but we must implement the method here because + // the AppKit code checks the first responder. + [[self _webView] toggleGrammarChecking:sender]; +} + + +static CGPoint coreGraphicsScreenPointForAppKitScreenPoint(NSPoint point) +{ + NSArray *screens = [NSScreen screens]; + + if ([screens count] == 0) { + // You could theoretically get here if running with no monitor, in which case it doesn't matter + // much where the "on-screen" point is. + return CGPointMake(point.x, point.y); + } + + // Flip the y coordinate from the top of the menu bar screen -- see 4636390 + return CGPointMake(point.x, NSMaxY([[screens objectAtIndex:0] frame]) - point.y); +} + +#endif + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + +- (void)orderFrontSubstitutionsPanel:(id)sender +{ + COMMAND_PROLOGUE + + NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker]; + if (!checker) { + LOG_ERROR("No NSSpellChecker"); + return; + } + + NSPanel *substitutionsPanel = [checker substitutionsPanel]; + if ([substitutionsPanel isVisible]) { + [substitutionsPanel orderOut:sender]; + return; + } + [substitutionsPanel orderFront:sender]; +} + +// FIXME 4799134: WebView is the bottleneck for this logic, but we must implement these methods here because +// the AppKit code checks the first responder. + +- (BOOL)smartInsertDeleteEnabled +{ + return [[self _webView] smartInsertDeleteEnabled]; +} + +- (void)setSmartInsertDeleteEnabled:(BOOL)flag +{ + [[self _webView] setSmartInsertDeleteEnabled:flag]; +} + +- (void)toggleSmartInsertDelete:(id)sender +{ + [[self _webView] toggleSmartInsertDelete:sender]; +} + +- (BOOL)isAutomaticQuoteSubstitutionEnabled +{ + return [[self _webView] isAutomaticQuoteSubstitutionEnabled]; +} + +- (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag +{ + [[self _webView] setAutomaticQuoteSubstitutionEnabled:flag]; +} + +- (void)toggleAutomaticQuoteSubstitution:(id)sender +{ + [[self _webView] toggleAutomaticQuoteSubstitution:sender]; +} + +- (BOOL)isAutomaticLinkDetectionEnabled +{ + return [[self _webView] isAutomaticLinkDetectionEnabled]; +} + +- (void)setAutomaticLinkDetectionEnabled:(BOOL)flag +{ + [[self _webView] setAutomaticLinkDetectionEnabled:flag]; +} + +- (void)toggleAutomaticLinkDetection:(id)sender +{ + [[self _webView] toggleAutomaticLinkDetection:sender]; +} + +- (BOOL)isAutomaticDashSubstitutionEnabled +{ + return [[self _webView] isAutomaticDashSubstitutionEnabled]; +} + +- (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag +{ + [[self _webView] setAutomaticDashSubstitutionEnabled:flag]; +} + +- (void)toggleAutomaticDashSubstitution:(id)sender +{ + [[self _webView] toggleAutomaticDashSubstitution:sender]; +} + +- (BOOL)isAutomaticTextReplacementEnabled +{ + return [[self _webView] isAutomaticTextReplacementEnabled]; +} + +- (void)setAutomaticTextReplacementEnabled:(BOOL)flag +{ + [[self _webView] setAutomaticTextReplacementEnabled:flag]; +} + +- (void)toggleAutomaticTextReplacement:(id)sender +{ + [[self _webView] toggleAutomaticTextReplacement:sender]; +} + +- (BOOL)isAutomaticSpellingCorrectionEnabled +{ + return [[self _webView] isAutomaticSpellingCorrectionEnabled]; +} + +- (void)setAutomaticSpellingCorrectionEnabled:(BOOL)flag +{ + [[self _webView] setAutomaticSpellingCorrectionEnabled:flag]; +} + +- (void)toggleAutomaticSpellingCorrection:(id)sender +{ + [[self _webView] toggleAutomaticSpellingCorrection:sender]; +} + +#endif + +- (void)_lookUpInDictionaryFromMenu:(id)sender +{ + // Dictionary API will accept a whitespace-only string and display UI as if it were real text, + // so bail out early to avoid that. + if ([[[self selectedString] _webkit_stringByTrimmingWhitespace] length] == 0) + return; + + NSAttributedString *attrString = [self selectedAttributedString]; + + Frame* coreFrame = core([self _frame]); + if (!coreFrame) + return; + + NSRect rect = coreFrame->selection()->bounds(); + +#ifndef BUILDING_ON_TIGER + NSDictionary *attributes = [attrString fontAttributesInRange:NSMakeRange(0,1)]; + NSFont *font = [attributes objectForKey:NSFontAttributeName]; + if (font) + rect.origin.y += [font ascender]; +#endif + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + [self showDefinitionForAttributedString:attrString atPoint:rect.origin]; + return; +#endif + + // We soft link to get the function that displays the dictionary (either pop-up window or app) to avoid the performance + // penalty of linking to another framework. This function changed signature as well as framework between Tiger and Leopard, + // so the two cases are handled separately. + +#ifdef BUILDING_ON_TIGER + typedef OSStatus (*ServiceWindowShowFunction)(id inWordString, NSRect inWordBoundary, UInt16 inLineDirection); + const char *frameworkPath = "/System/Library/Frameworks/ApplicationServices.framework/Frameworks/LangAnalysis.framework/LangAnalysis"; + const char *functionName = "DCMDictionaryServiceWindowShow"; +#else + typedef void (*ServiceWindowShowFunction)(id unusedDictionaryRef, id inWordString, CFRange selectionRange, id unusedFont, CGPoint textOrigin, Boolean verticalText, id unusedTransform); + const char *frameworkPath = "/System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/HIToolbox"; + const char *functionName = "HIDictionaryWindowShow"; +#endif + + static bool lookedForFunction = false; + static ServiceWindowShowFunction dictionaryServiceWindowShow = NULL; + + if (!lookedForFunction) { + void* langAnalysisFramework = dlopen(frameworkPath, RTLD_LAZY); + ASSERT(langAnalysisFramework); + if (langAnalysisFramework) + dictionaryServiceWindowShow = (ServiceWindowShowFunction)dlsym(langAnalysisFramework, functionName); + lookedForFunction = true; + } + + ASSERT(dictionaryServiceWindowShow); + if (!dictionaryServiceWindowShow) { + NSLog(@"Couldn't find the %s function in %s", functionName, frameworkPath); + return; + } + +#ifdef BUILDING_ON_TIGER + // FIXME: must check for right-to-left here + NSWritingDirection writingDirection = NSWritingDirectionLeftToRight; + + // FIXME: the dictionary API expects the rect for the first line of selection. Passing + // the rect for the entire selection, as we do here, positions the pop-up window near + // the bottom of the selection rather than at the selected word. + rect = [self convertRect:rect toView:nil]; + rect.origin = [[self window] convertBaseToScreen:rect.origin]; + NSData *data = [attrString RTFFromRange:NSMakeRange(0, [attrString length]) documentAttributes:nil]; + dictionaryServiceWindowShow(data, rect, (writingDirection == NSWritingDirectionRightToLeft) ? 1 : 0); +#else + // The HIDictionaryWindowShow function requires the origin, in CG screen coordinates, of the first character of text in the selection. + // FIXME 4945808: We approximate this in a way that works well when a single word is selected, and less well in some other cases + // (but no worse than we did in Tiger) + NSPoint windowPoint = [self convertPoint:rect.origin toView:nil]; + NSPoint screenPoint = [[self window] convertBaseToScreen:windowPoint]; + + dictionaryServiceWindowShow(nil, attrString, CFRangeMake(0, [attrString length]), nil, + coreGraphicsScreenPointForAppKitScreenPoint(screenPoint), false, nil); +#endif +} + +- (void)_hoverFeedbackSuspendedChanged +{ + [self _updateMouseoverWithFakeEvent]; +} + +- (BOOL)_interceptEditingKeyEvent:(KeyboardEvent*)event shouldSaveCommand:(BOOL)shouldSave +{ + // Ask AppKit to process the key event -- it will call back with either insertText or doCommandBySelector. + WebHTMLViewInterpretKeyEventsParameters parameters; + parameters.eventWasHandled = false; + parameters.shouldSaveCommand = shouldSave; + // If we're intercepting the initial IM call we assume that the IM has consumed the event, + // and only change this assumption if one of the NSTextInput/Responder callbacks is used. + // We assume the IM will *not* consume hotkey sequences + parameters.consumedByIM = !event->metaKey() && shouldSave; + + if (const PlatformKeyboardEvent* platformEvent = event->keyEvent()) { + NSEvent *macEvent = platformEvent->macEvent(); + if ([macEvent type] == NSKeyDown && [_private->completionController filterKeyDown:macEvent]) + return true; + + if ([macEvent type] == NSFlagsChanged) + return false; + + parameters.event = event; + _private->interpretKeyEventsParameters = ¶meters; + _private->receivedNOOP = NO; + const Vector<KeypressCommand>& commands = event->keypressCommands(); + bool hasKeypressCommand = !commands.isEmpty(); + + // FIXME: interpretKeyEvents doesn't match application key equivalents (such as Cmd+A), + // and sends noop: for those. As a result, we don't handle those from within WebCore, + // but send a full sequence of DOM events, including an unneeded keypress. + if (parameters.shouldSaveCommand || !hasKeypressCommand) + [self interpretKeyEvents:[NSArray arrayWithObject:macEvent]]; + else { + size_t size = commands.size(); + // Are there commands that would just cause text insertion if executed via Editor? + // WebKit doesn't have enough information about mode to decide how they should be treated, so we leave it upon WebCore + // to either handle them immediately (e.g. Tab that changes focus) or let a keypress event be generated + // (e.g. Tab that inserts a Tab character, or Enter). + bool haveTextInsertionCommands = false; + for (size_t i = 0; i < size; ++i) { + if ([self coreCommandBySelector:NSSelectorFromString(commands[i].commandName)].isTextInsertion()) + haveTextInsertionCommands = true; + } + if (!haveTextInsertionCommands || platformEvent->type() == PlatformKeyboardEvent::Char) { + for (size_t i = 0; i < size; ++i) { + if (commands[i].commandName == "insertText:") + [self insertText:commands[i].text]; + else + [self doCommandBySelector:NSSelectorFromString(commands[i].commandName)]; + } + } + } + _private->interpretKeyEventsParameters = 0; + } + return (!_private->receivedNOOP && parameters.eventWasHandled) || parameters.consumedByIM; +} + +- (WebCore::CachedImage*)promisedDragTIFFDataSource +{ + return _private->promisedDragTIFFDataSource; +} + +- (void)setPromisedDragTIFFDataSource:(WebCore::CachedImage*)source +{ + if (source) + source->addClient(promisedDataClient()); + + if (_private->promisedDragTIFFDataSource) + _private->promisedDragTIFFDataSource->removeClient(promisedDataClient()); + _private->promisedDragTIFFDataSource = source; +} + +#undef COMMAND_PROLOGUE + +- (void)_layoutIfNeeded +{ + ASSERT(!_private->subviewsSetAside); + + if ([self _needsLayout]) + [self layout]; +} + +- (void)_web_updateLayoutAndStyleIfNeededRecursive +{ + WebFrame *webFrame = [self _frame]; + Frame* coreFrame = core(webFrame); + if (coreFrame && coreFrame->view()) + coreFrame->view()->updateLayoutAndStyleIfNeededRecursive(); +} + +- (void) _destroyAllWebPlugins +{ + [[self _pluginController] destroyAllPlugins]; +} + +- (BOOL)_needsLayout +{ + return [[self _frame] _needsLayout]; +} + +#if USE(ACCELERATED_COMPOSITING) +- (void)attachRootLayer:(CALayer*)layer +{ + if (!_private->layerHostingView) { + NSView* hostingView = [[NSView alloc] initWithFrame:[self bounds]]; +#if !defined(BUILDING_ON_LEOPARD) + [hostingView setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; +#endif + [self addSubview:hostingView]; + [hostingView release]; + // hostingView is owned by being a subview of self + _private->layerHostingView = hostingView; + } + + // Make a container layer, which will get sized/positioned by AppKit and CA. + CALayer* viewLayer = [CALayer layer]; + +#if defined(BUILDING_ON_LEOPARD) + // Turn off default animations. + NSNull *nullValue = [NSNull null]; + NSDictionary *actions = [NSDictionary dictionaryWithObjectsAndKeys: + nullValue, @"anchorPoint", + nullValue, @"bounds", + nullValue, @"contents", + nullValue, @"contentsRect", + nullValue, @"opacity", + nullValue, @"position", + nullValue, @"sublayerTransform", + nullValue, @"sublayers", + nullValue, @"transform", + nil]; + [viewLayer setStyle:[NSDictionary dictionaryWithObject:actions forKey:@"actions"]]; +#endif + +#if !defined(BUILDING_ON_LEOPARD) + // If we aren't in the window yet, we'll use the screen's scale factor now, and reset the scale + // via -viewDidMoveToWindow. + CGFloat scaleFactor = [self window] ? [[self window] userSpaceScaleFactor] : [[NSScreen mainScreen] userSpaceScaleFactor]; + [viewLayer setTransform:CATransform3DMakeScale(scaleFactor, scaleFactor, 1)]; +#endif + + if ([self layer]) { + // If we are in a layer-backed view, we need to manually initialize the geometry for our layer. + [viewLayer setBounds:NSRectToCGRect([_private->layerHostingView bounds])]; + [viewLayer setAnchorPoint:CGPointMake(0, [self isFlipped] ? 1 : 0)]; + CGPoint layerPosition = NSPointToCGPoint([self convertPointToBase:[_private->layerHostingView frame].origin]); + [viewLayer setPosition:layerPosition]; + } + + [_private->layerHostingView setLayer:viewLayer]; + [_private->layerHostingView setWantsLayer:YES]; + + // Parent our root layer in the container layer + [viewLayer addSublayer:layer]; + + if ([[self _webView] _postsAcceleratedCompositingNotifications]) + [[NSNotificationCenter defaultCenter] postNotificationName:_WebViewDidStartAcceleratedCompositingNotification object:[self _webView] userInfo:nil]; + +#if defined(BUILDING_ON_LEOPARD) + [viewLayer setSublayerTransform:CATransform3DMakeScale(1, -1, 1)]; // setGeometryFlipped: doesn't exist on Leopard. + [self _updateLayerHostingViewPosition]; +#else + // Do geometry flipping here, which flips all the compositing layers so they are top-down. + [viewLayer setGeometryFlipped:YES]; +#endif +} + +- (void)detachRootLayer +{ + if (_private->layerHostingView) { + [_private->layerHostingView setLayer:nil]; + [_private->layerHostingView setWantsLayer:NO]; + [_private->layerHostingView removeFromSuperview]; + _private->layerHostingView = nil; + } +} + +#if defined(BUILDING_ON_LEOPARD) +// This method is necessary on Leopard to work around <rdar://problem/7067892>. +- (void)_updateLayerHostingViewPosition +{ + if (!_private->layerHostingView) + return; + + const CGFloat maxHeight = 2048; + NSRect layerViewFrame = [self bounds]; + + if (layerViewFrame.size.height > maxHeight) { + // Clamp the size of the view to <= maxHeight to avoid the bug. + layerViewFrame.size.height = maxHeight; + NSRect visibleRect = [[self enclosingScrollView] documentVisibleRect]; + + // Place the top of the layer-hosting view at the top of the visibleRect. + CGFloat topOffset = NSMinY(visibleRect); + layerViewFrame.origin.y = topOffset; + + // Compensate for the moved view by adjusting the sublayer transform on the view's layer (using flipped coords). + CATransform3D flipTransform = CATransform3DMakeTranslation(0, topOffset, 0); + flipTransform = CATransform3DScale(flipTransform, 1, -1, 1); + [[_private->layerHostingView layer] setSublayerTransform:flipTransform]; + } + + [_private->layerHostingView _updateLayerGeometryFromView]; // Workaround for <rdar://problem/7071636> + [_private->layerHostingView setFrame:layerViewFrame]; +} +#endif // defined(BUILDING_ON_LEOPARD) + +- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx +{ + if (_private) { + ASSERT(!_private->drawingIntoLayer); + _private->drawingIntoLayer = YES; + } + + [super drawLayer:layer inContext:ctx]; + + if (_private) + _private->drawingIntoLayer = NO; +} + +- (BOOL)_web_isDrawingIntoLayer +{ + return _private->drawingIntoLayer; +} + +#endif // USE(ACCELERATED_COMPOSITING) + +@end + +@implementation WebHTMLView (WebNSTextInputSupport) + +- (NSArray *)validAttributesForMarkedText +{ + static NSArray *validAttributes; + if (!validAttributes) { + validAttributes = [[NSArray alloc] initWithObjects: + NSUnderlineStyleAttributeName, NSUnderlineColorAttributeName, + NSMarkedClauseSegmentAttributeName, NSTextInputReplacementRangeAttributeName, nil]; + // NSText also supports the following attributes, but it's + // hard to tell which are really required for text input to + // work well; I have not seen any input method make use of them yet. + // NSFontAttributeName, NSForegroundColorAttributeName, + // NSBackgroundColorAttributeName, NSLanguageAttributeName. + CFRetain(validAttributes); + } + LOG(TextInput, "validAttributesForMarkedText -> (...)"); + return validAttributes; +} + +- (NSTextInputContext *)inputContext +{ + return _private->exposeInputContext ? [super inputContext] : nil; +} + +- (NSAttributedString *)textStorage +{ + if (!_private->exposeInputContext) { + LOG(TextInput, "textStorage -> nil"); + return nil; + } + NSAttributedString *result = [self attributedSubstringFromRange:NSMakeRange(0, UINT_MAX)]; + + LOG(TextInput, "textStorage -> \"%@\"", result ? [result string] : @""); + + // We have to return an empty string rather than null to prevent TSM from calling -string + return result ? result : [[[NSAttributedString alloc] initWithString:@""] autorelease]; +} + +- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint +{ + NSWindow *window = [self window]; + WebFrame *frame = [self _frame]; + + if (window) + thePoint = [window convertScreenToBase:thePoint]; + thePoint = [self convertPoint:thePoint fromView:nil]; + + DOMRange *range = [frame _characterRangeAtPoint:thePoint]; + if (!range) { + LOG(TextInput, "characterIndexForPoint:(%f, %f) -> NSNotFound", thePoint.x, thePoint.y); + return NSNotFound; + } + + unsigned result = [frame _convertDOMRangeToNSRange:range].location; + LOG(TextInput, "characterIndexForPoint:(%f, %f) -> %u", thePoint.x, thePoint.y, result); + return result; +} + +- (NSRect)firstRectForCharacterRange:(NSRange)theRange +{ + WebFrame *frame = [self _frame]; + + // Just to match NSTextView's behavior. Regression tests cannot detect this; + // to reproduce, use a test application from http://bugs.webkit.org/show_bug.cgi?id=4682 + // (type something; try ranges (1, -1) and (2, -1). + if ((theRange.location + theRange.length < theRange.location) && (theRange.location + theRange.length != 0)) + theRange.length = 0; + + DOMRange *range = [frame _convertNSRangeToDOMRange:theRange]; + if (!range) { + LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (0, 0, 0, 0)", theRange.location, theRange.length); + return NSMakeRect(0, 0, 0, 0); + } + + ASSERT([range startContainer]); + ASSERT([range endContainer]); + + NSRect resultRect = [frame _firstRectForDOMRange:range]; + resultRect = [self convertRect:resultRect toView:nil]; + + NSWindow *window = [self window]; + if (window) + resultRect.origin = [window convertBaseToScreen:resultRect.origin]; + + LOG(TextInput, "firstRectForCharacterRange:(%u, %u) -> (%f, %f, %f, %f)", theRange.location, theRange.length, resultRect.origin.x, resultRect.origin.y, resultRect.size.width, resultRect.size.height); + return resultRect; +} + +- (NSRange)selectedRange +{ + if (!isTextInput(core([self _frame]))) { + LOG(TextInput, "selectedRange -> (NSNotFound, 0)"); + return NSMakeRange(NSNotFound, 0); + } + NSRange result = [[self _frame] _selectedNSRange]; + + LOG(TextInput, "selectedRange -> (%u, %u)", result.location, result.length); + return result; +} + +- (NSRange)markedRange +{ + WebFrame *webFrame = [self _frame]; + Frame* coreFrame = core(webFrame); + if (!coreFrame) + return NSMakeRange(0, 0); + NSRange result = [webFrame _convertToNSRange:coreFrame->editor()->compositionRange().get()]; + + LOG(TextInput, "markedRange -> (%u, %u)", result.location, result.length); + return result; +} + +- (NSAttributedString *)attributedSubstringFromRange:(NSRange)nsRange +{ + WebFrame *frame = [self _frame]; + Frame* coreFrame = core(frame); + if (!isTextInput(coreFrame) || isInPasswordField(coreFrame)) { + LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length); + return nil; + } + DOMRange *domRange = [frame _convertNSRangeToDOMRange:nsRange]; + if (!domRange) { + LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> nil", nsRange.location, nsRange.length); + return nil; + } + + NSAttributedString *result = [NSAttributedString _web_attributedStringFromRange:core(domRange)]; + + // [NSAttributedString(WebKitExtras) _web_attributedStringFromRange:] insists on inserting a trailing + // whitespace at the end of the string which breaks the ATOK input method. <rdar://problem/5400551> + // To work around this we truncate the resultant string to the correct length. + if ([result length] > nsRange.length) { + ASSERT([result length] == nsRange.length + 1); + ASSERT([[result string] characterAtIndex:nsRange.length] == '\n' || [[result string] characterAtIndex:nsRange.length] == ' '); + result = [result attributedSubstringFromRange:NSMakeRange(0, nsRange.length)]; + } + LOG(TextInput, "attributedSubstringFromRange:(%u, %u) -> \"%@\"", nsRange.location, nsRange.length, [result string]); + return result; +} + +// test for 10.4 because of <rdar://problem/4243463> +#ifdef BUILDING_ON_TIGER +- (long)conversationIdentifier +{ + return (long)self; +} +#else +- (NSInteger)conversationIdentifier +{ + return (NSInteger)self; +} +#endif + +- (BOOL)hasMarkedText +{ + Frame* coreFrame = core([self _frame]); + BOOL result = coreFrame && coreFrame->editor()->hasComposition(); + LOG(TextInput, "hasMarkedText -> %u", result); + return result; +} + +- (void)unmarkText +{ + LOG(TextInput, "unmarkText"); + + // Use pointer to get parameters passed to us by the caller of interpretKeyEvents. + WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters; + _private->interpretKeyEventsParameters = 0; + + if (parameters) { + parameters->eventWasHandled = YES; + parameters->consumedByIM = NO; + } + + if (Frame* coreFrame = core([self _frame])) + coreFrame->editor()->confirmComposition(); +} + +static void extractUnderlines(NSAttributedString *string, Vector<CompositionUnderline>& result) +{ + int length = [[string string] length]; + + int i = 0; + while (i < length) { + NSRange range; + NSDictionary *attrs = [string attributesAtIndex:i longestEffectiveRange:&range inRange:NSMakeRange(i, length - i)]; + + if (NSNumber *style = [attrs objectForKey:NSUnderlineStyleAttributeName]) { + Color color = Color::black; + if (NSColor *colorAttr = [attrs objectForKey:NSUnderlineColorAttributeName]) + color = colorFromNSColor([colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]); + result.append(CompositionUnderline(range.location, NSMaxRange(range), color, [style intValue] > 1)); + } + + i = range.location + range.length; + } +} + +- (void)setMarkedText:(id)string selectedRange:(NSRange)newSelRange +{ + BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; // Otherwise, NSString + + LOG(TextInput, "setMarkedText:\"%@\" selectedRange:(%u, %u)", isAttributedString ? [string string] : string, newSelRange.location, newSelRange.length); + + // Use pointer to get parameters passed to us by the caller of interpretKeyEvents. + WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters; + _private->interpretKeyEventsParameters = 0; + + if (parameters) { + parameters->eventWasHandled = YES; + parameters->consumedByIM = NO; + } + + Frame* coreFrame = core([self _frame]); + if (!coreFrame) + return; + + if (![self _isEditable]) + return; + + Vector<CompositionUnderline> underlines; + NSString *text = string; + + if (isAttributedString) { + unsigned markedTextLength = [(NSString *)string length]; + NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:NULL inRange:NSMakeRange(0, markedTextLength)]; + LOG(TextInput, " ReplacementRange: %@", rangeString); + // The AppKit adds a 'secret' property to the string that contains the replacement range. + // The replacement range is the range of the the text that should be replaced with the new string. + if (rangeString) + [[self _frame] _selectNSRange:NSRangeFromString(rangeString)]; + + text = [string string]; + extractUnderlines(string, underlines); + } + + coreFrame->editor()->setComposition(text, underlines, newSelRange.location, NSMaxRange(newSelRange)); +} + +- (void)doCommandBySelector:(SEL)selector +{ + LOG(TextInput, "doCommandBySelector:\"%s\"", sel_getName(selector)); + + // Use pointer to get parameters passed to us by the caller of interpretKeyEvents. + // The same call to interpretKeyEvents can do more than one command. + WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters; + if (parameters) + parameters->consumedByIM = NO; + + if (selector == @selector(noop:)) { + _private->receivedNOOP = YES; + return; + } + + KeyboardEvent* event = parameters ? parameters->event : 0; + bool shouldSaveCommand = parameters && parameters->shouldSaveCommand; + + if (event && shouldSaveCommand) + event->keypressCommands().append(KeypressCommand(NSStringFromSelector(selector))); + else { + // Make sure that only direct calls to doCommandBySelector: see the parameters by setting to 0. + _private->interpretKeyEventsParameters = 0; + + bool eventWasHandled; + + WebView *webView = [self _webView]; + if ([[webView _editingDelegateForwarder] webView:webView doCommandBySelector:selector]) + eventWasHandled = true; + else { + Editor::Command command = [self coreCommandBySelector:selector]; + if (command.isSupported()) + eventWasHandled = command.execute(event); + else { + // If WebKit does not support this command, we need to pass the selector to super. + _private->selectorForDoCommandBySelector = selector; + + // The sink does two things: 1) Tells us if the responder went unhandled, and + // 2) prevents any NSBeep; we don't ever want to beep here. + WebResponderChainSink *sink = [[WebResponderChainSink alloc] initWithResponderChain:self]; + [super doCommandBySelector:selector]; + eventWasHandled = ![sink receivedUnhandledCommand]; + [sink detach]; + [sink release]; + + _private->selectorForDoCommandBySelector = 0; + } + } + + if (parameters) + parameters->eventWasHandled = eventWasHandled; + + // Restore the parameters so that other calls to doCommandBySelector: see them, + // and other commands can participate in setting the "eventWasHandled" flag. + _private->interpretKeyEventsParameters = parameters; + } +} + +- (void)insertText:(id)string +{ + BOOL isAttributedString = [string isKindOfClass:[NSAttributedString class]]; // Otherwise, NSString + + LOG(TextInput, "insertText:\"%@\"", isAttributedString ? [string string] : string); + + WebHTMLViewInterpretKeyEventsParameters* parameters = _private->interpretKeyEventsParameters; + _private->interpretKeyEventsParameters = 0; + if (parameters) + parameters->consumedByIM = NO; + + // We don't support inserting an attributed string but input methods don't appear to require this. + RefPtr<Frame> coreFrame = core([self _frame]); + NSString *text; + bool isFromInputMethod = coreFrame && coreFrame->editor()->hasComposition(); + if (isAttributedString) { + text = [string string]; + // We deal with the NSTextInputReplacementRangeAttributeName attribute from NSAttributedString here + // simply because it is used by at least one Input Method -- it corresonds to the kEventParamTextInputSendReplaceRange + // event in TSM. This behaviour matches that of -[WebHTMLView setMarkedText:selectedRange:] when it receives an + // NSAttributedString + NSString *rangeString = [string attribute:NSTextInputReplacementRangeAttributeName atIndex:0 longestEffectiveRange:NULL inRange:NSMakeRange(0, [text length])]; + LOG(TextInput, " ReplacementRange: %@", rangeString); + if (rangeString) { + [[self _frame] _selectNSRange:NSRangeFromString(rangeString)]; + isFromInputMethod = YES; + } + } else + text = string; + + bool eventHandled = false; + if ([text length]) { + KeyboardEvent* event = parameters ? parameters->event : 0; + + // insertText can be called from an input method or from normal key event processing + // If its from normal key event processing, we may need to save the action to perform it later. + // If its from an input method, then we should go ahead and insert the text now. + // We assume it's from the input method if we have marked text. + // FIXME: In theory, this could be wrong for some input methods, so we should try to find + // another way to determine if the call is from the input method + bool shouldSaveCommand = parameters && parameters->shouldSaveCommand; + if (event && shouldSaveCommand && !isFromInputMethod) { + event->keypressCommands().append(KeypressCommand("insertText:", text)); + _private->interpretKeyEventsParameters = parameters; + return; + } + + String eventText = text; + eventText.replace(NSBackTabCharacter, NSTabCharacter); // same thing is done in KeyEventMac.mm in WebCore + if (coreFrame && coreFrame->editor()->canEdit()) { + if (!coreFrame->editor()->hasComposition()) + eventHandled = coreFrame->editor()->insertText(eventText, event); + else { + eventHandled = true; + coreFrame->editor()->confirmComposition(eventText); + } + } + } + + if (!parameters) + return; + + if (isFromInputMethod) { + // Allow doCommandBySelector: to be called after insertText: by resetting interpretKeyEventsParameters + _private->interpretKeyEventsParameters = parameters; + parameters->consumedByIM = YES; + return; + } + + parameters->eventWasHandled = eventHandled; +} + +- (void)_updateSelectionForInputManager +{ + Frame* coreFrame = core([self _frame]); + if (!coreFrame) + return; + + BOOL exposeInputContext = isTextInput(coreFrame) && !isInPasswordField(coreFrame); + if (exposeInputContext != _private->exposeInputContext) { + _private->exposeInputContext = exposeInputContext; + // Let AppKit cache a potentially changed input context. + // WebCore routinely sets the selection to None when editing, and IMs become unhappy when an input context suddenly turns nil, see bug 26009. + if (!coreFrame->selection()->isNone()) + [NSApp updateWindows]; + } + + if (!coreFrame->editor()->hasComposition()) + return; + + if (coreFrame->editor()->ignoreCompositionSelectionChange()) + return; + + unsigned start; + unsigned end; + if (coreFrame->editor()->getCompositionSelection(start, end)) + [[NSInputManager currentInputManager] markedTextSelectionChanged:NSMakeRange(start, end - start) client:self]; + else { + coreFrame->editor()->confirmCompositionWithoutDisturbingSelection(); + [[NSInputManager currentInputManager] markedTextAbandoned:self]; + } +} + +@end + +@implementation WebHTMLView (WebDocumentPrivateProtocols) + +- (NSRect)selectionRect +{ + if (![self _hasSelection]) + return NSZeroRect; + return core([self _frame])->selection()->bounds(); +} + +- (NSArray *)selectionTextRects +{ + if (![self _hasSelection]) + return nil; + + Vector<FloatRect> list; + if (Frame* coreFrame = core([self _frame])) + coreFrame->selection()->getClippedVisibleTextRectangles(list); + + size_t size = list.size(); + + NSMutableArray *result = [NSMutableArray arrayWithCapacity:size]; + + for (size_t i = 0; i < size; ++i) + [result addObject:[NSValue valueWithRect:list[i]]]; + + return result; +} + +- (NSView *)selectionView +{ + return self; +} + +- (NSImage *)selectionImageForcingBlackText:(BOOL)forceBlackText +{ + if (![self _hasSelection]) + return nil; + return core([self _frame])->selectionImage(forceBlackText); +} + +- (NSRect)selectionImageRect +{ + if (![self _hasSelection]) + return NSZeroRect; + return core([self _frame])->selection()->bounds(); +} + +- (NSArray *)pasteboardTypesForSelection +{ + if ([self _canSmartCopyOrDelete]) { + NSMutableArray *types = [[[[self class] _selectionPasteboardTypes] mutableCopy] autorelease]; + [types addObject:WebSmartPastePboardType]; + return types; + } else { + return [[self class] _selectionPasteboardTypes]; + } +} + +- (void)writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard +{ + [self _writeSelectionWithPasteboardTypes:types toPasteboard:pasteboard cachedAttributedString:nil]; +} + +- (void)selectAll +{ + Frame* coreFrame = core([self _frame]); + if (coreFrame) + coreFrame->selection()->selectAll(); +} + +- (void)deselectAll +{ + Frame* coreFrame = core([self _frame]); + if (!coreFrame) + return; + coreFrame->selection()->clear(); +} + +- (NSString *)string +{ + return [[self _frame] _stringForRange:[self _documentRange]]; +} + +- (NSAttributedString *)_attributeStringFromDOMRange:(DOMRange *)range +{ + NSAttributedString *attributedString; +#if !LOG_DISABLED + double start = CFAbsoluteTimeGetCurrent(); +#endif + attributedString = [[[NSAttributedString alloc] _initWithDOMRange:range] autorelease]; +#if !LOG_DISABLED + double duration = CFAbsoluteTimeGetCurrent() - start; + LOG(Timing, "creating attributed string from selection took %f seconds.", duration); +#endif + return attributedString; +} + +- (NSAttributedString *)attributedString +{ + DOMDocument *document = [[self _frame] DOMDocument]; + NSAttributedString *attributedString = [self _attributeStringFromDOMRange:[document _documentRange]]; + if (!attributedString) { + Document* coreDocument = core(document); + attributedString = [NSAttributedString _web_attributedStringFromRange:Range::create(coreDocument, coreDocument, 0, coreDocument, coreDocument->childNodeCount()).get()]; + } + return attributedString; +} + +- (NSString *)selectedString +{ + return [[self _frame] _selectedString]; +} + +- (NSAttributedString *)selectedAttributedString +{ + NSAttributedString *attributedString = [self _attributeStringFromDOMRange:[self _selectedRange]]; + if (!attributedString) { + Frame* coreFrame = core([self _frame]); + if (coreFrame) { + RefPtr<Range> range = coreFrame->selection()->selection().toNormalizedRange(); + attributedString = [NSAttributedString _web_attributedStringFromRange:range.get()]; + } + } + return attributedString; +} + +- (BOOL)supportsTextEncoding +{ + return YES; +} + +- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection +{ + return [self _findString:string options:(forward ? 0 : WebFindOptionsBackwards) | (caseFlag ? 0 : WebFindOptionsCaseInsensitive) | (startInSelection ? WebFindOptionsStartInSelection : 0)]; +} + +@end + +@implementation WebHTMLView (WebDocumentInternalProtocols) + +- (NSDictionary *)elementAtPoint:(NSPoint)point +{ + return [self elementAtPoint:point allowShadowContent:NO]; +} + +- (NSDictionary *)elementAtPoint:(NSPoint)point allowShadowContent:(BOOL)allow +{ + Frame* coreFrame = core([self _frame]); + if (!coreFrame) + return nil; + return [[[WebElementDictionary alloc] initWithHitTestResult:coreFrame->eventHandler()->hitTestResultAtPoint(IntPoint(point), allow)] autorelease]; +} + +- (NSUInteger)countMatchesForText:(NSString *)string inDOMRange:(DOMRange *)range options:(WebFindOptions)options limit:(NSUInteger)limit markMatches:(BOOL)markMatches +{ + Frame* coreFrame = core([self _frame]); + if (!coreFrame) + return 0; + + return coreFrame->editor()->countMatchesForText(string, core(range), coreOptions(options), limit, markMatches); +} + +- (void)setMarkedTextMatchesAreHighlighted:(BOOL)newValue +{ + Frame* coreFrame = core([self _frame]); + if (!coreFrame) + return; + coreFrame->editor()->setMarkedTextMatchesAreHighlighted(newValue); +} + +- (BOOL)markedTextMatchesAreHighlighted +{ + Frame* coreFrame = core([self _frame]); + return coreFrame && coreFrame->editor()->markedTextMatchesAreHighlighted(); +} + +- (void)unmarkAllTextMatches +{ + Frame* coreFrame = core([self _frame]); + if (!coreFrame) + return; + Document* document = coreFrame->document(); + if (!document) + return; + document->markers()->removeMarkers(DocumentMarker::TextMatch); +} + +- (NSArray *)rectsForTextMatches +{ + Frame* coreFrame = core([self _frame]); + if (!coreFrame) + return [NSArray array]; + Document* document = coreFrame->document(); + if (!document) + return [NSArray array]; + + Vector<IntRect> rects = document->markers()->renderedRectsForMarkers(DocumentMarker::TextMatch); + unsigned count = rects.size(); + NSMutableArray *result = [NSMutableArray arrayWithCapacity:count]; + for (unsigned index = 0; index < count; ++index) + [result addObject:[NSValue valueWithRect:rects[index]]]; + return result; +} + +- (BOOL)_findString:(NSString *)string options:(WebFindOptions)options +{ + if (![string length]) + return NO; + Frame* coreFrame = core([self _frame]); + return coreFrame && coreFrame->editor()->findString(string, coreOptions(options)); +} + +@end + +// This is used by AppKit and is included here so that WebDataProtocolScheme is only defined once. +@implementation NSURL (WebDataURL) + ++ (NSURL *)_web_uniqueWebDataURL +{ + CFUUIDRef UUIDRef = CFUUIDCreate(kCFAllocatorDefault); + NSString *UUIDString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef); + CFRelease(UUIDRef); + NSURL *URL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@", WebDataProtocolScheme, UUIDString]]; + CFRelease(UUIDString); + return URL; +} + +@end + +@implementation WebResponderChainSink + +- (id)initWithResponderChain:(NSResponder *)chain +{ + self = [super init]; + _lastResponderInChain = chain; + while (NSResponder *next = [_lastResponderInChain nextResponder]) + _lastResponderInChain = next; + [_lastResponderInChain setNextResponder:self]; + return self; +} + +- (void)detach +{ + [_lastResponderInChain setNextResponder:nil]; + _lastResponderInChain = nil; +} + +- (BOOL)receivedUnhandledCommand +{ + return _receivedUnhandledCommand; +} + +- (void)noResponderFor:(SEL)selector +{ + _receivedUnhandledCommand = YES; +} + +- (void)doCommandBySelector:(SEL)selector +{ + _receivedUnhandledCommand = YES; +} + +- (BOOL)tryToPerform:(SEL)action with:(id)object +{ + _receivedUnhandledCommand = YES; + return YES; +} + +@end diff --git a/Source/WebKit/mac/WebView/WebHTMLViewInternal.h b/Source/WebKit/mac/WebView/WebHTMLViewInternal.h new file mode 100644 index 0000000..07a782a --- /dev/null +++ b/Source/WebKit/mac/WebView/WebHTMLViewInternal.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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. + */ + +// Things internal to the WebKit framework; not SPI. + +#import "WebHTMLViewPrivate.h" + +#if USE(ACCELERATED_COMPOSITING) +@class CALayer; +#endif +@class WebFrame; + +namespace WebCore { + class CachedImage; + class KeyboardEvent; +} + +@interface WebHTMLView (WebInternal) +- (void)_selectionChanged; +- (void)_updateFontPanel; +- (BOOL)_canSmartCopyOrDelete; + +- (id <WebHTMLHighlighter>)_highlighterForType:(NSString*)type; +- (WebFrame *)_frame; +- (void)_lookUpInDictionaryFromMenu:(id)sender; +- (void)_hoverFeedbackSuspendedChanged; +- (BOOL)_interceptEditingKeyEvent:(WebCore::KeyboardEvent *)event shouldSaveCommand:(BOOL)shouldSave; +- (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard; +- (NSEvent *)_mouseDownEvent; +#ifndef BUILDING_ON_TIGER +- (BOOL)isGrammarCheckingEnabled; +- (void)setGrammarCheckingEnabled:(BOOL)flag; +- (void)toggleGrammarChecking:(id)sender; +#endif +- (WebCore::CachedImage*)promisedDragTIFFDataSource; +- (void)setPromisedDragTIFFDataSource:(WebCore::CachedImage*)source; +- (void)_web_updateLayoutAndStyleIfNeededRecursive; +- (void)_destroyAllWebPlugins; +- (BOOL)_needsLayout; + +#if USE(ACCELERATED_COMPOSITING) +- (void)attachRootLayer:(CALayer*)layer; +- (void)detachRootLayer; +- (BOOL)_web_isDrawingIntoLayer; +#endif + +#if USE(ACCELERATED_COMPOSITING) && defined(BUILDING_ON_LEOPARD) +- (void)_updateLayerHostingViewPosition; +#endif + +@end diff --git a/Source/WebKit/mac/WebView/WebHTMLViewPrivate.h b/Source/WebKit/mac/WebView/WebHTMLViewPrivate.h new file mode 100644 index 0000000..9ee807d --- /dev/null +++ b/Source/WebKit/mac/WebView/WebHTMLViewPrivate.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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 <WebKit/WebHTMLView.h> + +#if !defined(ENABLE_NETSCAPE_PLUGIN_API) +#define ENABLE_NETSCAPE_PLUGIN_API 1 +#endif + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 +#define WebCGFloat float +#else +#define WebCGFloat CGFloat +#endif + +@class DOMDocumentFragment; +@class DOMNode; +@class DOMRange; +@class WebPluginController; + +@protocol WebHTMLHighlighter +- (NSRect)highlightRectForLine:(NSRect)lineRect representedNode:(DOMNode *)node; +- (void)paintHighlightForBox:(NSRect)boxRect onLine:(NSRect)lineRect behindText:(BOOL)text entireLine:(BOOL)line representedNode:(DOMNode *)node; +@end + +extern const float _WebHTMLViewPrintingMinimumShrinkFactor; +extern const float _WebHTMLViewPrintingMaximumShrinkFactor; + +@interface WebHTMLView (WebPrivate) + ++ (NSArray *)supportedMIMETypes; ++ (NSArray *)supportedImageMIMETypes; ++ (NSArray *)supportedNonImageMIMETypes; ++ (NSArray *)unsupportedTextMIMETypes; + +- (void)close; + +// Modifier (flagsChanged) tracking SPI ++ (void)_postFlagsChangedEvent:(NSEvent *)flagsChangedEvent; +- (void)_updateMouseoverWithFakeEvent; + +- (void)_setAsideSubviews; +- (void)_restoreSubviews; + +- (BOOL)_insideAnotherHTMLView; +- (void)_clearLastHitViewIfSelf; +- (void)_updateMouseoverWithEvent:(NSEvent *)event; + ++ (NSArray *)_insertablePasteboardTypes; ++ (NSArray *)_selectionPasteboardTypes; +- (void)_writeSelectionToPasteboard:(NSPasteboard *)pasteboard; + +- (void)_frameOrBoundsChanged; + +- (void)_handleAutoscrollForMouseDragged:(NSEvent *)event; +- (WebPluginController *)_pluginController; + +// FIXME: _selectionRect is deprecated in favor of selectionRect, which is in protocol WebDocumentSelection. +// We can't remove this yet because it's still in use by Mail. +- (NSRect)_selectionRect; + +- (void)_startAutoscrollTimer:(NSEvent *)event; +- (void)_stopAutoscrollTimer; + +- (BOOL)_canEdit; +- (BOOL)_canEditRichly; +- (BOOL)_canAlterCurrentSelection; +- (BOOL)_hasSelection; +- (BOOL)_hasSelectionOrInsertionPoint; +- (BOOL)_isEditable; + +- (BOOL)_transparentBackground; +- (void)_setTransparentBackground:(BOOL)isBackgroundTransparent; + +- (void)_setToolTip:(NSString *)string; + +// SPI used by Mail. +// FIXME: These should all be moved to WebView; we won't always have a WebHTMLView. +- (NSImage *)_selectionDraggingImage; +- (NSRect)_selectionDraggingRect; +- (DOMNode *)_insertOrderedList; +- (DOMNode *)_insertUnorderedList; +- (BOOL)_canIncreaseSelectionListLevel; +- (BOOL)_canDecreaseSelectionListLevel; +- (DOMNode *)_increaseSelectionListLevel; +- (DOMNode *)_increaseSelectionListLevelOrdered; +- (DOMNode *)_increaseSelectionListLevelUnordered; +- (void)_decreaseSelectionListLevel; +- (void)_setHighlighter:(id <WebHTMLHighlighter>)highlighter ofType:(NSString *)type; +- (void)_removeHighlighterOfType:(NSString *)type; +- (DOMDocumentFragment *)_documentFragmentFromPasteboard:(NSPasteboard *)pasteboard forType:(NSString *)pboardType inContext:(DOMRange *)context subresources:(NSArray **)subresources; + +#if ENABLE_NETSCAPE_PLUGIN_API +- (void)_resumeNullEventsForAllNetscapePlugins; +- (void)_pauseNullEventsForAllNetscapePlugins; +#endif + +- (BOOL)_isUsingAcceleratedCompositing; +- (NSView *)_compositingLayersHostingView; + +// SPI for printing (should be converted to API someday). When the WebHTMLView isn't being printed +// directly, this method must be called before paginating, or the computed height might be incorrect. +// Typically this would be called from inside an override of -[NSView knowsPageRange:]. +- (void)_layoutForPrinting; +- (WebCGFloat)_adjustedBottomOfPageWithTop:(WebCGFloat)top bottom:(WebCGFloat)bottom limit:(WebCGFloat)bottomLimit; +- (BOOL)_isInPrintMode; +- (BOOL)_beginPrintModeWithPageWidth:(float)pageWidth height:(float)pageHeight shrinkToFit:(BOOL)shrinkToFit; +// Lays out to pages of the given minimum width and height or more (increasing both dimensions proportionally) +// as needed for the content to fit, but no more than the given maximum width. +- (BOOL)_beginPrintModeWithMinimumPageWidth:(WebCGFloat)minimumPageWidth height:(WebCGFloat)minimumPageHeight maximumPageWidth:(WebCGFloat)maximumPageWidth; +- (void)_endPrintMode; + +- (BOOL)_isInScreenPaginationMode; +- (BOOL)_beginScreenPaginationModeWithPageSize:(CGSize)pageSize shrinkToFit:(BOOL)shrinkToFit; +- (void)_endScreenPaginationMode; + +- (BOOL)_canSmartReplaceWithPasteboard:(NSPasteboard *)pasteboard; + +@end + +#undef WebCGFloat diff --git a/Source/WebKit/mac/WebView/WebHistoryDelegate.h b/Source/WebKit/mac/WebView/WebHistoryDelegate.h new file mode 100644 index 0000000..4415365 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebHistoryDelegate.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR + * 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 <Foundation/Foundation.h> + +@class WebFrame; +@class WebNavigationData; +@class WebView; + +@interface NSObject (WebHistoryDelegate) + +- (void)webView:(WebView *)webView didNavigateWithNavigationData:(WebNavigationData *)navigationData inFrame:(WebFrame *)webFrame; + +- (void)webView:(WebView *)webView didPerformClientRedirectFromURL:(NSString *)sourceURL toURL:(NSString *)destinationURL inFrame:(WebFrame *)webFrame; + +- (void)webView:(WebView *)webView didPerformServerRedirectFromURL:(NSString *)sourceURL toURL:(NSString *)destinationURL inFrame:(WebFrame *)webFrame; + +- (void)webView:(WebView *)webView updateHistoryTitle:(NSString *)title forURL:(NSString *)url; + +- (void)populateVisitedLinksForWebView:(WebView *)webView; + +@end diff --git a/Source/WebKit/mac/WebView/WebJSPDFDoc.h b/Source/WebKit/mac/WebView/WebJSPDFDoc.h new file mode 100644 index 0000000..d3e756c --- /dev/null +++ b/Source/WebKit/mac/WebView/WebJSPDFDoc.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR + * 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 <JavaScriptCore/JSBase.h> + +@class WebDataSource; + +JSObjectRef makeJSPDFDoc(JSContextRef, WebDataSource *); diff --git a/Source/WebKit/mac/WebView/WebJSPDFDoc.mm b/Source/WebKit/mac/WebView/WebJSPDFDoc.mm new file mode 100644 index 0000000..24169fe --- /dev/null +++ b/Source/WebKit/mac/WebView/WebJSPDFDoc.mm @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR + * 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 "WebJSPDFDoc.h" + +#import "WebDataSource.h" +#import "WebDelegateImplementationCaching.h" +#import "WebFrame.h" +#import "WebView.h" +#import <JavaScriptCore/JSObjectRef.h> + +static void jsPDFDocInitialize(JSContextRef ctx, JSObjectRef object) +{ + WebDataSource *dataSource = (WebDataSource *)JSObjectGetPrivate(object); + CFRetain(dataSource); +} + +static void jsPDFDocFinalize(JSObjectRef object) +{ + WebDataSource *dataSource = (WebDataSource *)JSObjectGetPrivate(object); + CFRelease(dataSource); +} + +static JSValueRef jsPDFDocPrint(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + WebDataSource *dataSource = (WebDataSource *)JSObjectGetPrivate(thisObject); + + WebView *webView = [[dataSource webFrame] webView]; + CallUIDelegate(webView, @selector(webView:printFrameView:), [[dataSource webFrame] frameView]); + + return JSValueMakeUndefined(ctx); +} + +static JSStaticFunction jsPDFDocStaticFunctions[] = { + { "print", jsPDFDocPrint, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { 0, 0, 0 }, +}; + +static JSClassDefinition jsPDFDocClassDefinition = { + 0, + kJSClassAttributeNone, + "Doc", + 0, + 0, + jsPDFDocStaticFunctions, + jsPDFDocInitialize, jsPDFDocFinalize, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +JSObjectRef makeJSPDFDoc(JSContextRef ctx, WebDataSource *dataSource) +{ + static JSClassRef jsPDFDocClass = JSClassCreate(&jsPDFDocClassDefinition); + + return JSObjectMake(ctx, jsPDFDocClass, dataSource); +} diff --git a/Source/WebKit/mac/WebView/WebNavigationData.h b/Source/WebKit/mac/WebView/WebNavigationData.h new file mode 100644 index 0000000..227df16 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebNavigationData.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR + * 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 <Foundation/Foundation.h> + +@class WebFrame; +@class WebNavigationDataPrivate; +@class WebView; + +@interface WebNavigationData : NSObject +{ +@private + WebNavigationDataPrivate *_private; +} + +- (id)initWithURLString:(NSString *)url title:(NSString *)title originalRequest:(NSURLRequest *)request response:(NSURLResponse *)response hasSubstituteData:(BOOL)hasSubstituteData clientRedirectSource:(NSString *)redirectSource; +- (NSString *)url; +- (NSString *)title; +- (NSURLRequest *)originalRequest; +- (NSURLResponse *)response; +- (BOOL)hasSubstituteData; +- (NSString *)clientRedirectSource; + +@end diff --git a/Source/WebKit/mac/WebView/WebNavigationData.mm b/Source/WebKit/mac/WebView/WebNavigationData.mm new file mode 100644 index 0000000..753a441 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebNavigationData.mm @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR + * 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 "WebNavigationData.h" + +@interface WebNavigationDataPrivate : NSObject +{ +@public + NSString *url; + NSString *title; + NSURLRequest *originalRequest; + NSURLResponse *response; + BOOL hasSubstituteData; + NSString *clientRedirectSource; +} + +@end + +@implementation WebNavigationDataPrivate + +- (void)dealloc +{ + [url release]; + [title release]; + [originalRequest release]; + [response release]; + [clientRedirectSource release]; + + [super dealloc]; +} + +@end + +@implementation WebNavigationData + +- (id)initWithURLString:(NSString *)url title:(NSString *)title originalRequest:(NSURLRequest *)request response:(NSURLResponse *)response hasSubstituteData:(BOOL)hasSubstituteData clientRedirectSource:(NSString *)redirectSource +{ + _private = [[WebNavigationDataPrivate alloc] init]; + + _private->url = [url retain]; + _private->title = [title retain]; + _private->originalRequest = [request retain]; + _private->response = [response retain]; + _private->hasSubstituteData = hasSubstituteData; + _private->clientRedirectSource = [redirectSource retain]; + + return self; +} + +- (NSString *)url +{ + return _private->url; +} + +- (NSString *)title +{ + return _private->title; +} + +- (NSURLRequest *)originalRequest +{ + return _private->originalRequest; +} + +- (NSURLResponse *)response +{ + return _private->response; +} + +- (BOOL)hasSubstituteData +{ + return _private->hasSubstituteData; +} + +- (NSString *)clientRedirectSource +{ + return _private->clientRedirectSource; +} + +- (void)dealloc +{ + [_private release]; + [super dealloc]; +} + +@end diff --git a/Source/WebKit/mac/WebView/WebPDFDocumentExtras.h b/Source/WebKit/mac/WebView/WebPDFDocumentExtras.h new file mode 100644 index 0000000..0dce43e --- /dev/null +++ b/Source/WebKit/mac/WebView/WebPDFDocumentExtras.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR + * 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. + */ + +@class PDFDocument; + +NSArray *allScriptsInPDFDocument(PDFDocument *); diff --git a/Source/WebKit/mac/WebView/WebPDFDocumentExtras.mm b/Source/WebKit/mac/WebView/WebPDFDocumentExtras.mm new file mode 100644 index 0000000..7651dba --- /dev/null +++ b/Source/WebKit/mac/WebView/WebPDFDocumentExtras.mm @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR + * 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 "WebPDFDocumentExtras.h" + +#import "WebTypesInternal.h" +#import <JavaScriptCore/Vector.h> +#import <JavaScriptCore/RetainPtr.h> +#import <PDFKit/PDFDocument.h> +#import <objc/objc-runtime.h> + +#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) +@interface PDFDocument (Internal) +- (CGPDFDocumentRef)documentRef; +@end +#endif + +static void appendValuesInPDFNameSubtreeToVector(CGPDFDictionaryRef subtree, Vector<CGPDFObjectRef>& values) +{ + CGPDFArrayRef names; + if (CGPDFDictionaryGetArray(subtree, "Names", &names)) { + size_t nameCount = CGPDFArrayGetCount(names) / 2; + for (size_t i = 0; i < nameCount; ++i) { + CGPDFObjectRef object; + CGPDFArrayGetObject(names, 2 * i + 1, &object); + values.append(object); + } + return; + } + + CGPDFArrayRef kids; + if (!CGPDFDictionaryGetArray(subtree, "Kids", &kids)) + return; + + size_t kidCount = CGPDFArrayGetCount(kids); + for (size_t i = 0; i < kidCount; ++i) { + CGPDFDictionaryRef kid; + if (!CGPDFArrayGetDictionary(kids, i, &kid)) + continue; + appendValuesInPDFNameSubtreeToVector(kid, values); + } +} + +static void getAllValuesInPDFNameTree(CGPDFDictionaryRef tree, Vector<CGPDFObjectRef>& allValues) +{ + appendValuesInPDFNameSubtreeToVector(tree, allValues); +} + +NSArray *allScriptsInPDFDocument(PDFDocument *document) +{ + NSMutableArray *scripts = [NSMutableArray array]; + CGPDFDocumentRef pdfDocument = [document documentRef]; + if (!pdfDocument) + return scripts; + + CGPDFDictionaryRef pdfCatalog = CGPDFDocumentGetCatalog(pdfDocument); + if (!pdfCatalog) + return scripts; + + // Get the dictionary of all document-level name trees. + CGPDFDictionaryRef namesDictionary; + if (!CGPDFDictionaryGetDictionary(pdfCatalog, "Names", &namesDictionary)) + return scripts; + + // Get the document-level "JavaScript" name tree. + CGPDFDictionaryRef javaScriptNameTree; + if (!CGPDFDictionaryGetDictionary(namesDictionary, "JavaScript", &javaScriptNameTree)) + return scripts; + + // The names are aribtrary. We are only interested in the values. + Vector<CGPDFObjectRef> objects; + getAllValuesInPDFNameTree(javaScriptNameTree, objects); + size_t objectCount = objects.size(); + + for (size_t i = 0; i < objectCount; ++i) { + CGPDFDictionaryRef javaScriptAction; + if (!CGPDFObjectGetValue(reinterpret_cast<CGPDFObjectRef>(objects[i]), kCGPDFObjectTypeDictionary, &javaScriptAction)) + continue; + + // A JavaScript action must have an action type of "JavaScript". + const char* actionType; + if (!CGPDFDictionaryGetName(javaScriptAction, "S", &actionType) || strcmp(actionType, "JavaScript")) + continue; + + const UInt8* bytes = 0; + CFIndex length; + CGPDFStreamRef stream; + CGPDFStringRef string; + RetainPtr<CFDataRef> data; + if (CGPDFDictionaryGetStream(javaScriptAction, "JS", &stream)) { + CGPDFDataFormat format; + data.adoptCF(CGPDFStreamCopyData(stream, &format)); + if (!data) + continue; + bytes = CFDataGetBytePtr(data.get()); + length = CFDataGetLength(data.get()); + } else if (CGPDFDictionaryGetString(javaScriptAction, "JS", &string)) { + bytes = CGPDFStringGetBytePtr(string); + length = CGPDFStringGetLength(string); + } + if (!bytes) + continue; + + NSStringEncoding encoding = (length > 1 && bytes[0] == 0xFE && bytes[1] == 0xFF) ? NSUnicodeStringEncoding : NSUTF8StringEncoding; + NSString *script = [[NSString alloc] initWithBytes:bytes length:length encoding:encoding]; + [scripts addObject:script]; + [script release]; + } + + return scripts; +} diff --git a/Source/WebKit/mac/WebView/WebPDFRepresentation.h b/Source/WebKit/mac/WebView/WebPDFRepresentation.h new file mode 100644 index 0000000..be40ae1 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebPDFRepresentation.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * 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 "WebDocument.h" + +@protocol WebDocumentRepresentation; + +@interface WebPDFRepresentation : NSObject <WebDocumentRepresentation> ++ (NSArray *)supportedMIMETypes; +@end diff --git a/Source/WebKit/mac/WebView/WebPDFRepresentation.mm b/Source/WebKit/mac/WebView/WebPDFRepresentation.mm new file mode 100644 index 0000000..44a1362 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebPDFRepresentation.mm @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. + * + * 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 "WebPDFRepresentation.h" + +#import "WebDataSourcePrivate.h" +#import "WebFrame.h" +#import "WebJSPDFDoc.h" +#import "WebNSObjectExtras.h" +#import "WebPDFDocumentExtras.h" +#import "WebPDFView.h" +#import "WebTypesInternal.h" +#import <JavaScriptCore/Assertions.h> +#import <JavaScriptCore/JSContextRef.h> +#import <JavaScriptCore/JSStringRef.h> +#import <JavaScriptCore/JSStringRefCF.h> + +@implementation WebPDFRepresentation + ++ (NSArray *)postScriptMIMETypes +{ + return [NSArray arrayWithObjects: + @"application/postscript", + nil]; +} + ++ (NSArray *)supportedMIMETypes +{ + return [[[self class] postScriptMIMETypes] arrayByAddingObjectsFromArray: + [NSArray arrayWithObjects: + @"text/pdf", + @"application/pdf", + nil]]; +} + ++ (Class)PDFDocumentClass +{ + static Class PDFDocumentClass = nil; + if (PDFDocumentClass == nil) { + PDFDocumentClass = [[WebPDFView PDFKitBundle] classNamed:@"PDFDocument"]; + if (PDFDocumentClass == nil) { + LOG_ERROR("Couldn't find PDFDocument class in PDFKit.framework"); + } + } + return PDFDocumentClass; +} + +- (void)setDataSource:(WebDataSource *)dataSource +{ +} + +- (void)receivedData:(NSData *)data withDataSource:(WebDataSource *)dataSource +{ +} + +- (void)receivedError:(NSError *)error withDataSource:(WebDataSource *)dataSource +{ +} + +- (NSData *)convertPostScriptDataSourceToPDF:(NSData *)data +{ + // Convert PostScript to PDF using Quartz 2D API + // http://developer.apple.com/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_ps_convert/chapter_16_section_1.html + + CGPSConverterCallbacks callbacks = { 0, 0, 0, 0, 0, 0, 0, 0 }; + CGPSConverterRef converter = CGPSConverterCreate(0, &callbacks, 0); + ASSERT(converter); + + CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)data); + ASSERT(provider); + + CFMutableDataRef result = CFDataCreateMutable(kCFAllocatorDefault, 0); + ASSERT(result); + + CGDataConsumerRef consumer = CGDataConsumerCreateWithCFData(result); + ASSERT(consumer); + + // Error handled by detecting zero-length 'result' in caller + CGPSConverterConvert(converter, provider, consumer, 0); + + CFRelease(converter); + CFRelease(provider); + CFRelease(consumer); + + return WebCFAutorelease(result); +} + +- (void)finishedLoadingWithDataSource:(WebDataSource *)dataSource +{ + NSData *data = [dataSource data]; + + NSArray *postScriptMIMETypes = [[self class] postScriptMIMETypes]; + NSString *mimeType = [dataSource _responseMIMEType]; + if ([postScriptMIMETypes containsObject:mimeType]) { + data = [self convertPostScriptDataSourceToPDF:data]; + if ([data length] == 0) + return; + } + + WebPDFView *view = (WebPDFView *)[[[dataSource webFrame] frameView] documentView]; + PDFDocument *doc = [[[[self class] PDFDocumentClass] alloc] initWithData:data]; + [view setPDFDocument:doc]; + + NSArray *scripts = allScriptsInPDFDocument(doc); + [doc release]; + doc = nil; + + NSUInteger scriptCount = [scripts count]; + if (!scriptCount) + return; + + JSGlobalContextRef ctx = JSGlobalContextCreate(0); + JSObjectRef jsPDFDoc = makeJSPDFDoc(ctx, dataSource); + + for (NSUInteger i = 0; i < scriptCount; ++i) { + JSStringRef script = JSStringCreateWithCFString((CFStringRef)[scripts objectAtIndex:i]); + JSEvaluateScript(ctx, script, jsPDFDoc, 0, 0, 0); + JSStringRelease(script); + } + + JSGlobalContextRelease(ctx); +} + +- (BOOL)canProvideDocumentSource +{ + return NO; +} + + +- (NSString *)documentSource +{ + return nil; +} + + +- (NSString *)title +{ + return nil; +} + +@end diff --git a/Source/WebKit/mac/WebView/WebPDFView.h b/Source/WebKit/mac/WebView/WebPDFView.h new file mode 100644 index 0000000..e480a1b --- /dev/null +++ b/Source/WebKit/mac/WebView/WebPDFView.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2005, 2006, 2007 Apple Computer, Inc. All rights reserved. + * + * 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 <WebKit/WebDocumentInternal.h> + +@class PDFDocument; +@class PDFView; +@class WebDataSource; + +@interface WebPDFView : NSView <WebDocumentView, WebDocumentSearching, WebDocumentIncrementalSearching, WebMultipleTextMatches, WebDocumentSelection, WebDocumentElement, WebDocumentPDF, _WebDocumentViewState, _WebDocumentZooming> +{ + NSView *previewView; + PDFView *PDFSubview; + NSString *path; + BOOL firstResponderIsPDFDocumentView; + BOOL written; + BOOL _ignoreScaleAndDisplayModeAndPageNotifications; + BOOL _willUpdatePreferencesSoon; + PDFView *PDFSubviewProxy; + WebDataSource *dataSource; + NSArray *textMatches; + NSPoint lastScrollPosition; +} + ++ (NSArray *)supportedMIMETypes; ++ (NSBundle *)PDFKitBundle; + +- (void)setPDFDocument:(PDFDocument *)doc; +- (PDFDocument *)PDFDocument; + +@end diff --git a/Source/WebKit/mac/WebView/WebPDFView.mm b/Source/WebKit/mac/WebView/WebPDFView.mm new file mode 100644 index 0000000..445b537 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebPDFView.mm @@ -0,0 +1,1509 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * 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 "WebPDFView.h" + +#import "DOMNodeInternal.h" +#import "DOMRangeInternal.h" +#import "WebDataSourceInternal.h" +#import "WebDelegateImplementationCaching.h" +#import "WebDocumentInternal.h" +#import "WebDocumentPrivate.h" +#import "WebFrame.h" +#import "WebFrameInternal.h" +#import "WebFrameView.h" +#import "WebLocalizableStrings.h" +#import "WebNSArrayExtras.h" +#import "WebNSAttributedStringExtras.h" +#import "WebNSPasteboardExtras.h" +#import "WebNSViewExtras.h" +#import "WebPDFRepresentation.h" +#import "WebPreferencesPrivate.h" +#import "WebUIDelegate.h" +#import "WebUIDelegatePrivate.h" +#import "WebView.h" +#import "WebViewInternal.h" +#import <PDFKit/PDFKit.h> +#import <WebCore/EventNames.h> +#import <WebCore/FormState.h> +#import <WebCore/Frame.h> +#import <WebCore/FrameLoadRequest.h> +#import <WebCore/FrameLoader.h> +#import <WebCore/HTMLFormElement.h> +#import <WebCore/HTMLFrameOwnerElement.h> +#import <WebCore/KURL.h> +#import <WebCore/KeyboardEvent.h> +#import <WebCore/MouseEvent.h> +#import <WebCore/PlatformKeyboardEvent.h> +#import <WebCore/RuntimeApplicationChecks.h> +#import <wtf/Assertions.h> + +using namespace WebCore; + +// Redeclarations of PDFKit notifications. We can't use the API since we use a weak link to the framework. +#define _webkit_PDFViewDisplayModeChangedNotification @"PDFViewDisplayModeChanged" +#define _webkit_PDFViewScaleChangedNotification @"PDFViewScaleChanged" +#define _webkit_PDFViewPageChangedNotification @"PDFViewChangedPage" + +@interface PDFDocument (PDFKitSecretsIKnow) +- (NSPrintOperation *)getPrintOperationForPrintInfo:(NSPrintInfo *)printInfo autoRotate:(BOOL)doRotate; +@end + +extern "C" NSString *_NSPathForSystemFramework(NSString *framework); + +@interface WebPDFView (FileInternal) ++ (Class)_PDFPreviewViewClass; ++ (Class)_PDFViewClass; +- (BOOL)_anyPDFTagsFoundInMenu:(NSMenu *)menu; +- (void)_applyPDFDefaults; +- (BOOL)_canLookUpInDictionary; +- (NSClipView *)_clipViewForPDFDocumentView; +- (NSEvent *)_fakeKeyEventWithFunctionKey:(unichar)functionKey; +- (NSMutableArray *)_menuItemsFromPDFKitForEvent:(NSEvent *)theEvent; +- (PDFSelection *)_nextMatchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag fromSelection:(PDFSelection *)initialSelection startInSelection:(BOOL)startInSelection; +- (void)_openWithFinder:(id)sender; +- (NSString *)_path; +- (void)_PDFDocumentViewMightHaveScrolled:(NSNotification *)notification; +- (BOOL)_pointIsInSelection:(NSPoint)point; +- (NSAttributedString *)_scaledAttributedString:(NSAttributedString *)unscaledAttributedString; +- (void)_setTextMatches:(NSArray *)array; +- (NSString *)_temporaryPDFDirectoryPath; +- (void)_trackFirstResponder; +- (void)_updatePreferencesSoon; +- (NSSet *)_visiblePDFPages; +@end; + +// PDFPrefUpdatingProxy is a class that forwards everything it gets to a target and updates the PDF viewing prefs +// after each of those messages. We use it as a way to hook all the places that the PDF viewing attrs change. +@interface PDFPrefUpdatingProxy : NSProxy { + WebPDFView *view; +} +- (id)initWithView:(WebPDFView *)view; +@end + +// MARK: C UTILITY FUNCTIONS + +static void _applicationInfoForMIMEType(NSString *type, NSString **name, NSImage **image) +{ + NSURL *appURL = nil; + + OSStatus error = LSCopyApplicationForMIMEType((CFStringRef)type, kLSRolesAll, (CFURLRef *)&appURL); + if (error != noErr) + return; + + NSString *appPath = [appURL path]; + CFRelease (appURL); + + *image = [[NSWorkspace sharedWorkspace] iconForFile:appPath]; + [*image setSize:NSMakeSize(16.f,16.f)]; + + NSString *appName = [[NSFileManager defaultManager] displayNameAtPath:appPath]; + *name = appName; +} + +// FIXME 4182876: We can eliminate this function in favor if -isEqual: if [PDFSelection isEqual:] is overridden +// to compare contents. +static BOOL _PDFSelectionsAreEqual(PDFSelection *selectionA, PDFSelection *selectionB) +{ + NSArray *aPages = [selectionA pages]; + NSArray *bPages = [selectionB pages]; + + if (![aPages isEqual:bPages]) + return NO; + + int count = [aPages count]; + int i; + for (i = 0; i < count; ++i) { + NSRect aBounds = [selectionA boundsForPage:[aPages objectAtIndex:i]]; + NSRect bBounds = [selectionB boundsForPage:[bPages objectAtIndex:i]]; + if (!NSEqualRects(aBounds, bBounds)) { + return NO; + } + } + + return YES; +} + +@implementation WebPDFView + +// MARK: WebPDFView API + ++ (NSBundle *)PDFKitBundle +{ + static NSBundle *PDFKitBundle = nil; + if (PDFKitBundle == nil) { + NSString *PDFKitPath = [_NSPathForSystemFramework(@"Quartz.framework") stringByAppendingString:@"/Frameworks/PDFKit.framework"]; + if (PDFKitPath == nil) { + LOG_ERROR("Couldn't find PDFKit.framework"); + return nil; + } + PDFKitBundle = [NSBundle bundleWithPath:PDFKitPath]; + if (![PDFKitBundle load]) { + LOG_ERROR("Couldn't load PDFKit.framework"); + } + } + return PDFKitBundle; +} + ++ (NSArray *)supportedMIMETypes +{ + return [WebPDFRepresentation supportedMIMETypes]; +} + +- (void)setPDFDocument:(PDFDocument *)doc +{ + // Both setDocument: and _applyPDFDefaults will trigger scale and mode-changed notifications. + // Those aren't reflecting user actions, so we need to ignore them. + _ignoreScaleAndDisplayModeAndPageNotifications = YES; + [PDFSubview setDocument:doc]; + [self _applyPDFDefaults]; + _ignoreScaleAndDisplayModeAndPageNotifications = NO; +} + +- (PDFDocument *)PDFDocument +{ + return [PDFSubview document]; +} + +// MARK: NSObject OVERRIDES + +- (void)dealloc +{ + [dataSource release]; + [previewView release]; + [PDFSubview setDelegate:nil]; + [PDFSubview release]; + [path release]; + [PDFSubviewProxy release]; + [textMatches release]; + [super dealloc]; +} + +// MARK: NSResponder OVERRIDES + +- (void)centerSelectionInVisibleArea:(id)sender +{ + [PDFSubview scrollSelectionToVisible:nil]; +} + +- (void)scrollPageDown:(id)sender +{ + // PDFView doesn't support this responder method directly, so we pass it a fake key event + [PDFSubview keyDown:[self _fakeKeyEventWithFunctionKey:NSPageDownFunctionKey]]; +} + +- (void)scrollPageUp:(id)sender +{ + // PDFView doesn't support this responder method directly, so we pass it a fake key event + [PDFSubview keyDown:[self _fakeKeyEventWithFunctionKey:NSPageUpFunctionKey]]; +} + +- (void)scrollLineDown:(id)sender +{ + // PDFView doesn't support this responder method directly, so we pass it a fake key event + [PDFSubview keyDown:[self _fakeKeyEventWithFunctionKey:NSDownArrowFunctionKey]]; +} + +- (void)scrollLineUp:(id)sender +{ + // PDFView doesn't support this responder method directly, so we pass it a fake key event + [PDFSubview keyDown:[self _fakeKeyEventWithFunctionKey:NSUpArrowFunctionKey]]; +} + +- (void)scrollToBeginningOfDocument:(id)sender +{ + // PDFView doesn't support this responder method directly, so we pass it a fake key event + [PDFSubview keyDown:[self _fakeKeyEventWithFunctionKey:NSHomeFunctionKey]]; +} + +- (void)scrollToEndOfDocument:(id)sender +{ + // PDFView doesn't support this responder method directly, so we pass it a fake key event + [PDFSubview keyDown:[self _fakeKeyEventWithFunctionKey:NSEndFunctionKey]]; +} + +// jumpToSelection is the old name for what AppKit now calls centerSelectionInVisibleArea. Safari +// was using the old jumpToSelection selector in its menu. Newer versions of Safari will us the +// selector centerSelectionInVisibleArea. We'll leave this old selector in place for two reasons: +// (1) compatibility between older Safari and newer WebKit; (2) other WebKit-based applications +// might be using the jumpToSelection: selector, and we don't want to break them. +- (void)jumpToSelection:(id)sender +{ + [self centerSelectionInVisibleArea:nil]; +} + +// MARK: NSView OVERRIDES + +- (BOOL)acceptsFirstResponder { + return YES; +} + +- (BOOL)becomeFirstResponder +{ + // This works together with setNextKeyView to splice our PDFSubview into + // the key loop similar to the way NSScrollView does this. + NSWindow *window = [self window]; + id newFirstResponder = nil; + + if ([window keyViewSelectionDirection] == NSSelectingPrevious) { + NSView *previousValidKeyView = [self previousValidKeyView]; + if ((previousValidKeyView != self) && (previousValidKeyView != PDFSubview)) + newFirstResponder = previousValidKeyView; + } else { + NSView *PDFDocumentView = [PDFSubview documentView]; + if ([PDFDocumentView acceptsFirstResponder]) + newFirstResponder = PDFDocumentView; + } + + if (!newFirstResponder) + return NO; + + if (![window makeFirstResponder:newFirstResponder]) + return NO; + + [[dataSource webFrame] _clearSelectionInOtherFrames]; + + return YES; +} + +- (NSView *)hitTest:(NSPoint)point +{ + // Override hitTest so we can override menuForEvent. + NSEvent *event = [NSApp currentEvent]; + NSEventType type = [event type]; + if (type == NSRightMouseDown || (type == NSLeftMouseDown && ([event modifierFlags] & NSControlKeyMask))) + return self; + + return [super hitTest:point]; +} + +- (id)initWithFrame:(NSRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + [self setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + + Class previewViewClass = [[self class] _PDFPreviewViewClass]; + + // We might not have found a previewViewClass, but if we did find it + // then we should be able to create an instance. + if (previewViewClass) { + previewView = [[previewViewClass alloc] initWithFrame:frame]; + ASSERT(previewView); + } + + NSView *topLevelPDFKitView = nil; + if (previewView) { + // We'll retain the PDFSubview here so that it is equally retained in all + // code paths. That way we don't need to worry about conditionally releasing + // it later. + PDFSubview = [[previewView performSelector:@selector(pdfView)] retain]; + topLevelPDFKitView = previewView; + } else { + PDFSubview = [[[[self class] _PDFViewClass] alloc] initWithFrame:frame]; + topLevelPDFKitView = PDFSubview; + } + + ASSERT(PDFSubview); + + [topLevelPDFKitView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [self addSubview:topLevelPDFKitView]; + + [PDFSubview setDelegate:self]; + written = NO; + // Messaging this proxy is the same as messaging PDFSubview, with the side effect that the + // PDF viewing defaults are updated afterwards + PDFSubviewProxy = (PDFView *)[[PDFPrefUpdatingProxy alloc] initWithView:self]; + } + + return self; +} + +- (NSMenu *)menuForEvent:(NSEvent *)theEvent +{ + // Start with the menu items supplied by PDFKit, with WebKit tags applied + NSMutableArray *items = [self _menuItemsFromPDFKitForEvent:theEvent]; + + // Add in an "Open with <default PDF viewer>" item + NSString *appName = nil; + NSImage *appIcon = nil; + + _applicationInfoForMIMEType([dataSource _responseMIMEType], &appName, &appIcon); + if (!appName) + appName = UI_STRING("Finder", "Default application name for Open With context menu"); + + // To match the PDFKit style, we'll add Open with Preview even when there's no document yet to view, and + // disable it using validateUserInterfaceItem. + NSString *title = [NSString stringWithFormat:UI_STRING("Open with %@", "context menu item for PDF"), appName]; + NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:title action:@selector(_openWithFinder:) keyEquivalent:@""]; + [item setTag:WebMenuItemTagOpenWithDefaultApplication]; + if (appIcon) + [item setImage:appIcon]; + [items insertObject:item atIndex:0]; + [item release]; + + [items insertObject:[NSMenuItem separatorItem] atIndex:1]; + + // pass the items off to the WebKit context menu mechanism + WebView *webView = [[dataSource webFrame] webView]; + ASSERT(webView); + NSMenu *menu = [webView _menuForElement:[self elementAtPoint:[self convertPoint:[theEvent locationInWindow] fromView:nil]] defaultItems:items]; + + // The delegate has now had the opportunity to add items to the standard PDF-related items, or to + // remove or modify some of the PDF-related items. In 10.4, the PDF context menu did not go through + // the standard WebKit delegate path, and so the standard PDF-related items always appeared. For + // clients that create their own context menu by hand-picking specific items from the default list, such as + // Safari, none of the PDF-related items will appear until the client is rewritten to explicitly + // include these items. For backwards compatibility of tip-of-tree WebKit with the 10.4 version of Safari + // (the configuration that people building open source WebKit use), we'll use the entire set of PDFKit-supplied + // menu items. This backward-compatibility hack won't work with any non-Safari clients, but this seems OK since + // (1) the symptom is fairly minor, and (2) we suspect that non-Safari clients are probably using the entire + // set of default items, rather than manually choosing from them. We can remove this code entirely when we + // ship a version of Safari that includes the fix for radar 3796579. + if (![self _anyPDFTagsFoundInMenu:menu] && applicationIsSafari()) { + [menu addItem:[NSMenuItem separatorItem]]; + NSEnumerator *e = [items objectEnumerator]; + NSMenuItem *menuItem; + while ((menuItem = [e nextObject]) != nil) { + // copy menuItem since a given menuItem can be in only one menu at a time, and we don't + // want to mess with the menu returned from PDFKit. + [menu addItem:[menuItem copy]]; + } + } + + return menu; +} + +- (void)setNextKeyView:(NSView *)aView +{ + // This works together with becomeFirstResponder to splice PDFSubview into + // the key loop similar to the way NSScrollView and NSClipView do this. + NSView *documentView = [PDFSubview documentView]; + if (documentView) { + [documentView setNextKeyView:aView]; + + // We need to make the documentView be the next view in the keyview loop. + // It would seem more sensible to do this in our init method, but it turns out + // that [NSClipView setDocumentView] won't call this method if our next key view + // is already set, so we wait until we're called before adding this connection. + // We'll also clear it when we're called with nil, so this could go through the + // same code path more than once successfully. + [super setNextKeyView: aView ? documentView : nil]; + } else + [super setNextKeyView:aView]; +} + +- (void)viewDidMoveToWindow +{ + // FIXME 2573089: we can observe a notification for first responder changes + // instead of the very frequent NSWindowDidUpdateNotification if/when 2573089 is addressed. + NSWindow *newWindow = [self window]; + if (!newWindow) + return; + + [self _trackFirstResponder]; + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter addObserver:self + selector:@selector(_trackFirstResponder) + name:NSWindowDidUpdateNotification + object:newWindow]; + + [notificationCenter addObserver:self + selector:@selector(_scaleOrDisplayModeOrPageChanged:) + name:_webkit_PDFViewScaleChangedNotification + object:PDFSubview]; + + [notificationCenter addObserver:self + selector:@selector(_scaleOrDisplayModeOrPageChanged:) + name:_webkit_PDFViewDisplayModeChangedNotification + object:PDFSubview]; + + [notificationCenter addObserver:self + selector:@selector(_scaleOrDisplayModeOrPageChanged:) + name:_webkit_PDFViewPageChangedNotification + object:PDFSubview]; + + [notificationCenter addObserver:self + selector:@selector(_PDFDocumentViewMightHaveScrolled:) + name:NSViewBoundsDidChangeNotification + object:[self _clipViewForPDFDocumentView]]; +} + +- (void)viewWillMoveToWindow:(NSWindow *)window +{ + // FIXME 2573089: we can observe a notification for changes to the first responder + // instead of the very frequent NSWindowDidUpdateNotification if/when 2573089 is addressed. + NSWindow *oldWindow = [self window]; + if (!oldWindow) + return; + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter removeObserver:self + name:NSWindowDidUpdateNotification + object:oldWindow]; + [notificationCenter removeObserver:self + name:_webkit_PDFViewScaleChangedNotification + object:PDFSubview]; + [notificationCenter removeObserver:self + name:_webkit_PDFViewDisplayModeChangedNotification + object:PDFSubview]; + [notificationCenter removeObserver:self + name:_webkit_PDFViewPageChangedNotification + object:PDFSubview]; + + [notificationCenter removeObserver:self + name:NSViewBoundsDidChangeNotification + object:[self _clipViewForPDFDocumentView]]; + + firstResponderIsPDFDocumentView = NO; +} + +// MARK: NSUserInterfaceValidations PROTOCOL IMPLEMENTATION + +- (BOOL)validateUserInterfaceItemWithoutDelegate:(id <NSValidatedUserInterfaceItem>)item +{ + SEL action = [item action]; + if (action == @selector(takeFindStringFromSelection:) || action == @selector(centerSelectionInVisibleArea:) || action == @selector(jumpToSelection:)) + return [PDFSubview currentSelection] != nil; + + if (action == @selector(_openWithFinder:)) + return [PDFSubview document] != nil; + + if (action == @selector(_lookUpInDictionaryFromMenu:)) + return [self _canLookUpInDictionary]; + + return YES; +} + +- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item +{ + // This can be called during teardown when _webView is nil. Return NO when this happens, because CallUIDelegateReturningBoolean + // assumes the WebVIew is non-nil. + if (![self _webView]) + return NO; + BOOL result = [self validateUserInterfaceItemWithoutDelegate:item]; + return CallUIDelegateReturningBoolean(result, [self _webView], @selector(webView:validateUserInterfaceItem:defaultValidation:), item, result); +} + +// MARK: INTERFACE BUILDER ACTIONS FOR SAFARI + +// Surprisingly enough, this isn't defined in any superclass, though it is defined in assorted AppKit classes since +// it's a standard menu item IBAction. +- (IBAction)copy:(id)sender +{ + [PDFSubview copy:sender]; +} + +// This used to be a standard IBAction (for Use Selection For Find), but AppKit now uses performFindPanelAction: +// with a menu item tag for this purpose. +- (IBAction)takeFindStringFromSelection:(id)sender +{ + [NSPasteboard _web_setFindPasteboardString:[[PDFSubview currentSelection] string] withOwner:self]; +} + +// MARK: WebFrameView UNDECLARED "DELEGATE METHODS" + +// This is tested in -[WebFrameView canPrintHeadersAndFooters], but isn't declared anywhere (yuck) +- (BOOL)canPrintHeadersAndFooters +{ + return NO; +} + +// This is tested in -[WebFrameView printOperationWithPrintInfo:], but isn't declared anywhere (yuck) +- (NSPrintOperation *)printOperationWithPrintInfo:(NSPrintInfo *)printInfo +{ + return [[PDFSubview document] getPrintOperationForPrintInfo:printInfo autoRotate:YES]; +} + +// MARK: WebDocumentView PROTOCOL IMPLEMENTATION + +- (void)setDataSource:(WebDataSource *)ds +{ + if (dataSource == ds) + return; + + dataSource = [ds retain]; + + // FIXME: There must be some better place to put this. There is no comment in ChangeLog + // explaining why it's in this method. + [self setFrame:[[self superview] frame]]; +} + +- (void)dataSourceUpdated:(WebDataSource *)dataSource +{ +} + +- (void)setNeedsLayout:(BOOL)flag +{ +} + +- (void)layout +{ +} + +- (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow +{ +} + +- (void)viewDidMoveToHostWindow +{ +} + +// MARK: WebDocumentElement PROTOCOL IMPLEMENTATION + +- (NSDictionary *)elementAtPoint:(NSPoint)point +{ + WebFrame *frame = [dataSource webFrame]; + ASSERT(frame); + + return [NSDictionary dictionaryWithObjectsAndKeys: + frame, WebElementFrameKey, + [NSNumber numberWithBool:[self _pointIsInSelection:point]], WebElementIsSelectedKey, + nil]; +} + +- (NSDictionary *)elementAtPoint:(NSPoint)point allowShadowContent:(BOOL)allow +{ + return [self elementAtPoint:point]; +} + +// MARK: WebDocumentSearching PROTOCOL IMPLEMENTATION + +- (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]; +} + +// MARK: WebDocumentIncrementalSearching PROTOCOL IMPLEMENTATION + +- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection +{ + PDFSelection *selection = [self _nextMatchFor:string direction:forward caseSensitive:caseFlag wrap:wrapFlag fromSelection:[PDFSubview currentSelection] startInSelection:startInSelection]; + if (!selection) + return NO; + + [PDFSubview setCurrentSelection:selection]; + [PDFSubview scrollSelectionToVisible:nil]; + return YES; +} + +// MARK: WebMultipleTextMatches PROTOCOL IMPLEMENTATION + +- (void)setMarkedTextMatchesAreHighlighted:(BOOL)newValue +{ + // This method is part of the WebMultipleTextMatches algorithm, but this class doesn't support + // highlighting text matches inline. +#ifndef NDEBUG + if (newValue) + LOG_ERROR("[WebPDFView setMarkedTextMatchesAreHighlighted:] called with YES, which isn't supported"); +#endif +} + +- (BOOL)markedTextMatchesAreHighlighted +{ + return NO; +} + +static BOOL isFrameInRange(WebFrame *frame, DOMRange *range) +{ + BOOL inRange = NO; + for (HTMLFrameOwnerElement* ownerElement = core(frame)->ownerElement(); ownerElement; ownerElement = ownerElement->document()->frame()->ownerElement()) { + if (ownerElement->document() == core(range)->ownerDocument()) { + inRange = [range intersectsNode:kit(ownerElement)]; + break; + } + } + return inRange; +} + +- (NSUInteger)countMatchesForText:(NSString *)string inDOMRange:(DOMRange *)range options:(WebFindOptions)options limit:(NSUInteger)limit markMatches:(BOOL)markMatches +{ + if (range && !isFrameInRange([dataSource webFrame], range)) + return 0; + + PDFSelection *previousMatch = nil; + NSMutableArray *matches = [[NSMutableArray alloc] initWithCapacity:limit]; + + for (;;) { + PDFSelection *nextMatch = [self _nextMatchFor:string direction:YES caseSensitive:!(options & WebFindOptionsCaseInsensitive) wrap:NO fromSelection:previousMatch startInSelection:NO]; + if (!nextMatch) + break; + + [matches addObject:nextMatch]; + previousMatch = nextMatch; + + if ([matches count] >= limit) + break; + } + + [self _setTextMatches:matches]; + [matches release]; + + return [matches count]; +} + +- (void)unmarkAllTextMatches +{ + [self _setTextMatches:nil]; +} + +- (NSArray *)rectsForTextMatches +{ + NSMutableArray *result = [NSMutableArray arrayWithCapacity:[textMatches count]]; + NSSet *visiblePages = [self _visiblePDFPages]; + NSEnumerator *matchEnumerator = [textMatches objectEnumerator]; + PDFSelection *match; + + while ((match = [matchEnumerator nextObject]) != nil) { + NSEnumerator *pages = [[match pages] objectEnumerator]; + PDFPage *page; + while ((page = [pages nextObject]) != nil) { + + // Skip pages that aren't visible (needed for non-continuous modes, see 5362989) + if (![visiblePages containsObject:page]) + continue; + + NSRect selectionOnPageInPDFViewCoordinates = [PDFSubview convertRect:[match boundsForPage:page] fromPage:page]; + [result addObject:[NSValue valueWithRect:selectionOnPageInPDFViewCoordinates]]; + } + } + + return result; +} + +// MARK: WebDocumentText PROTOCOL IMPLEMENTATION + +- (BOOL)supportsTextEncoding +{ + return NO; +} + +- (NSString *)string +{ + return [[PDFSubview document] string]; +} + +- (NSAttributedString *)attributedString +{ + // changing the selection is a hack, but the only way to get an attr string is via PDFSelection + + // must copy this selection object because we change the selection which seems to release it + PDFSelection *savedSelection = [[PDFSubview currentSelection] copy]; + [PDFSubview selectAll:nil]; + NSAttributedString *result = [[PDFSubview currentSelection] attributedString]; + if (savedSelection) { + [PDFSubview setCurrentSelection:savedSelection]; + [savedSelection release]; + } else { + // FIXME: behavior of setCurrentSelection:nil is not documented - check 4182934 for progress + // Otherwise, we could collapse this code with the case above. + [PDFSubview clearSelection]; + } + + result = [self _scaledAttributedString:result]; + + return result; +} + +- (NSString *)selectedString +{ + return [[PDFSubview currentSelection] string]; +} + +- (NSAttributedString *)selectedAttributedString +{ + return [self _scaledAttributedString:[[PDFSubview currentSelection] attributedString]]; +} + +- (void)selectAll +{ + [PDFSubview selectAll:nil]; +} + +- (void)deselectAll +{ + [PDFSubview clearSelection]; +} + +// MARK: WebDocumentViewState PROTOCOL IMPLEMENTATION + +// Even though to WebKit we are the "docView", in reality a PDFView contains its own scrollview and docView. +// And it even turns out there is another PDFKit view between the docView and its enclosing ScrollView, so +// we have to be sure to do our calculations based on that view, immediately inside the ClipView. We try +// to make as few assumptions about the PDFKit view hierarchy as possible. + +- (NSPoint)scrollPoint +{ + NSView *realDocView = [PDFSubview documentView]; + NSClipView *clipView = [[realDocView enclosingScrollView] contentView]; + return [clipView bounds].origin; +} + +- (void)setScrollPoint:(NSPoint)p +{ + WebFrame *frame = [dataSource webFrame]; + //FIXME: We only restore scroll state in the non-frames case because otherwise we get a crash due to + // PDFKit calling display from within its drawRect:. See bugzilla 4164. + if (![frame parentFrame]) { + NSView *realDocView = [PDFSubview documentView]; + [[[realDocView enclosingScrollView] documentView] scrollPoint:p]; + } +} + +- (id)viewState +{ + NSMutableArray *state = [NSMutableArray arrayWithCapacity:4]; + PDFDisplayMode mode = [PDFSubview displayMode]; + [state addObject:[NSNumber numberWithInt:mode]]; + if (mode == kPDFDisplaySinglePage || mode == kPDFDisplayTwoUp) { + unsigned int pageIndex = [[PDFSubview document] indexForPage:[PDFSubview currentPage]]; + [state addObject:[NSNumber numberWithUnsignedInt:pageIndex]]; + } // else in continuous modes, scroll position gets us to the right page + BOOL autoScaleFlag = [PDFSubview autoScales]; + [state addObject:[NSNumber numberWithBool:autoScaleFlag]]; + if (!autoScaleFlag) + [state addObject:[NSNumber numberWithFloat:[PDFSubview scaleFactor]]]; + + return state; +} + +- (void)setViewState:(id)statePList +{ + ASSERT([statePList isKindOfClass:[NSArray class]]); + NSArray *state = statePList; + int i = 0; + PDFDisplayMode mode = [[state objectAtIndex:i++] intValue]; + [PDFSubview setDisplayMode:mode]; + if (mode == kPDFDisplaySinglePage || mode == kPDFDisplayTwoUp) { + unsigned int pageIndex = [[state objectAtIndex:i++] unsignedIntValue]; + [PDFSubview goToPage:[[PDFSubview document] pageAtIndex:pageIndex]]; + } // else in continuous modes, scroll position gets us to the right page + BOOL autoScaleFlag = [[state objectAtIndex:i++] boolValue]; + [PDFSubview setAutoScales:autoScaleFlag]; + if (!autoScaleFlag) + [PDFSubview setScaleFactor:[[state objectAtIndex:i++] floatValue]]; +} + +// MARK: _WebDocumentTextSizing PROTOCOL IMPLEMENTATION + +- (IBAction)_zoomOut:(id)sender +{ + [PDFSubviewProxy zoomOut:sender]; +} + +- (IBAction)_zoomIn:(id)sender +{ + [PDFSubviewProxy zoomIn:sender]; +} + +- (IBAction)_resetZoom:(id)sender +{ + [PDFSubviewProxy setScaleFactor:1.0f]; +} + +- (BOOL)_canZoomOut +{ + return [PDFSubview canZoomOut]; +} + +- (BOOL)_canZoomIn +{ + return [PDFSubview canZoomIn]; +} + +- (BOOL)_canResetZoom +{ + return [PDFSubview scaleFactor] != 1.0; +} + +// MARK: WebDocumentSelection PROTOCOL IMPLEMENTATION + +- (NSRect)selectionRect +{ + NSRect result = NSZeroRect; + PDFSelection *selection = [PDFSubview currentSelection]; + NSEnumerator *pages = [[selection pages] objectEnumerator]; + PDFPage *page; + while ((page = [pages nextObject]) != nil) { + NSRect selectionOnPageInPDFViewCoordinates = [PDFSubview convertRect:[selection boundsForPage:page] fromPage:page]; + if (NSIsEmptyRect(result)) + result = selectionOnPageInPDFViewCoordinates; + else + result = NSUnionRect(result, selectionOnPageInPDFViewCoordinates); + } + + // Convert result to be in documentView (selectionView) coordinates + result = [PDFSubview convertRect:result toView:[PDFSubview documentView]]; + + return result; +} + +- (NSArray *)selectionTextRects +{ + // FIXME: We'd need new PDFKit API/SPI to get multiple text rects for selections that intersect more than one line + return [NSArray arrayWithObject:[NSValue valueWithRect:[self selectionRect]]]; +} + +- (NSView *)selectionView +{ + return [PDFSubview documentView]; +} + +- (NSImage *)selectionImageForcingBlackText:(BOOL)forceBlackText +{ + // Convert the selection to an attributed string, and draw that. + // FIXME 4621154: this doesn't handle italics (and maybe other styles) + // FIXME 4604366: this doesn't handle text at non-actual size + NSMutableAttributedString *attributedString = [[self selectedAttributedString] mutableCopy]; + NSRange wholeStringRange = NSMakeRange(0, [attributedString length]); + + // Modify the styles in the attributed string to draw black text, no background, and no underline. We draw + // no underline because it would look ugly. + [attributedString beginEditing]; + [attributedString removeAttribute:NSBackgroundColorAttributeName range:wholeStringRange]; + [attributedString removeAttribute:NSUnderlineStyleAttributeName range:wholeStringRange]; + if (forceBlackText) + [attributedString addAttribute:NSForegroundColorAttributeName value:[NSColor colorWithDeviceWhite:0.0f alpha:1.0f] range:wholeStringRange]; + [attributedString endEditing]; + + NSImage* selectionImage = [[[NSImage alloc] initWithSize:[self selectionRect].size] autorelease]; + + [selectionImage lockFocus]; + [attributedString drawAtPoint:NSZeroPoint]; + [selectionImage unlockFocus]; + + [attributedString release]; + + return selectionImage; +} + +- (NSRect)selectionImageRect +{ + // FIXME: deal with clipping? + return [self selectionRect]; +} + +- (NSArray *)pasteboardTypesForSelection +{ + return [NSArray arrayWithObjects:NSRTFDPboardType, NSRTFPboardType, NSStringPboardType, nil]; +} + +- (void)writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard +{ + NSAttributedString *attributedString = [self selectedAttributedString]; + + if ([types containsObject:NSRTFDPboardType]) { + NSData *RTFDData = [attributedString RTFDFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil]; + [pasteboard setData:RTFDData forType:NSRTFDPboardType]; + } + + if ([types containsObject:NSRTFPboardType]) { + if ([attributedString containsAttachments]) + attributedString = [attributedString _web_attributedStringByStrippingAttachmentCharacters]; + + NSData *RTFData = [attributedString RTFFromRange:NSMakeRange(0, [attributedString length]) documentAttributes:nil]; + [pasteboard setData:RTFData forType:NSRTFPboardType]; + } + + if ([types containsObject:NSStringPboardType]) + [pasteboard setString:[self selectedString] forType:NSStringPboardType]; +} + +// MARK: PDFView DELEGATE METHODS + +- (void)PDFViewWillClickOnLink:(PDFView *)sender withURL:(NSURL *)URL +{ + if (!URL) + return; + + NSWindow *window = [sender window]; + NSEvent *nsEvent = [window currentEvent]; + const int noButton = -1; + int button = noButton; + RefPtr<Event> event; + switch ([nsEvent type]) { + case NSLeftMouseUp: + button = 0; + break; + case NSRightMouseUp: + button = 1; + break; + case NSOtherMouseUp: + button = [nsEvent buttonNumber]; + break; + case NSKeyDown: { + PlatformKeyboardEvent pe(nsEvent); + pe.disambiguateKeyDownEvent(PlatformKeyboardEvent::RawKeyDown); + event = KeyboardEvent::create(eventNames().keydownEvent, true, true, 0, + pe.keyIdentifier(), pe.windowsVirtualKeyCode(), + pe.ctrlKey(), pe.altKey(), pe.shiftKey(), pe.metaKey(), false); + } + default: + break; + } + if (button != noButton) { + event = MouseEvent::create(eventNames().clickEvent, true, true, 0, [nsEvent clickCount], 0, 0, 0, 0, + [nsEvent modifierFlags] & NSControlKeyMask, + [nsEvent modifierFlags] & NSAlternateKeyMask, + [nsEvent modifierFlags] & NSShiftKeyMask, + [nsEvent modifierFlags] & NSCommandKeyMask, + button, 0, 0, true); + } + + // Call to the frame loader because this is where our security checks are made. + Frame* frame = core([dataSource webFrame]); + frame->loader()->loadFrameRequest(FrameLoadRequest(frame->document()->securityOrigin(), ResourceRequest(URL)), false, false, event.get(), 0, SendReferrer); +} + +- (void)PDFViewOpenPDFInNativeApplication:(PDFView *)sender +{ + // Delegate method sent when the user requests opening the PDF file in the system's default app + [self _openWithFinder:sender]; +} + +- (void)PDFViewPerformPrint:(PDFView *)sender +{ + CallUIDelegate([self _webView], @selector(webView:printFrameView:), [[dataSource webFrame] frameView]); +} + +- (void)PDFViewSavePDFToDownloadFolder:(PDFView *)sender +{ + // We don't want to write the file until we have a document to write (see 5267607). + if (![PDFSubview document]) { + NSBeep(); + return; + } + + // Delegate method sent when the user requests downloading the PDF file to disk. We pass NO for + // showingPanel: so that the PDF file is saved to the standard location without user intervention. + CallUIDelegate([self _webView], @selector(webView:saveFrameView:showingPanel:), [[dataSource webFrame] frameView], NO); +} + +@end + +@implementation WebPDFView (FileInternal) + ++ (Class)_PDFPreviewViewClass +{ + static Class PDFPreviewViewClass = nil; + static BOOL checkedForPDFPreviewViewClass = NO; + + if (!checkedForPDFPreviewViewClass) { + checkedForPDFPreviewViewClass = YES; + PDFPreviewViewClass = [[WebPDFView PDFKitBundle] classNamed:@"PDFPreviewView"]; + } + + // This class might not be available; callers need to deal with a nil return here. + return PDFPreviewViewClass; +} + ++ (Class)_PDFViewClass +{ + static Class PDFViewClass = nil; + if (PDFViewClass == nil) { + PDFViewClass = [[WebPDFView PDFKitBundle] classNamed:@"PDFView"]; + if (!PDFViewClass) + LOG_ERROR("Couldn't find PDFView class in PDFKit.framework"); + } + return PDFViewClass; +} + +- (BOOL)_anyPDFTagsFoundInMenu:(NSMenu *)menu +{ + NSEnumerator *e = [[menu itemArray] objectEnumerator]; + NSMenuItem *item; + while ((item = [e nextObject]) != nil) { + switch ([item tag]) { + case WebMenuItemTagOpenWithDefaultApplication: + case WebMenuItemPDFActualSize: + case WebMenuItemPDFZoomIn: + case WebMenuItemPDFZoomOut: + case WebMenuItemPDFAutoSize: + case WebMenuItemPDFSinglePage: + case WebMenuItemPDFSinglePageScrolling: + case WebMenuItemPDFFacingPages: + case WebMenuItemPDFFacingPagesScrolling: + case WebMenuItemPDFContinuous: + case WebMenuItemPDFNextPage: + case WebMenuItemPDFPreviousPage: + return YES; + } + } + return NO; +} + +- (void)_applyPDFDefaults +{ + // Set up default viewing params + WebPreferences *prefs = [[dataSource _webView] preferences]; + float scaleFactor = [prefs PDFScaleFactor]; + if (scaleFactor == 0) + [PDFSubview setAutoScales:YES]; + else { + [PDFSubview setAutoScales:NO]; + [PDFSubview setScaleFactor:scaleFactor]; + } + [PDFSubview setDisplayMode:[prefs PDFDisplayMode]]; +} + +- (BOOL)_canLookUpInDictionary +{ + return [PDFSubview respondsToSelector:@selector(_searchInDictionary:)]; +} + +- (NSClipView *)_clipViewForPDFDocumentView +{ + NSClipView *clipView = (NSClipView *)[[PDFSubview documentView] _web_superviewOfClass:[NSClipView class]]; + ASSERT(clipView); + return clipView; +} + +- (NSEvent *)_fakeKeyEventWithFunctionKey:(unichar)functionKey +{ + // FIXME 4400480: when PDFView implements the standard scrolling selectors that this + // method is used to mimic, we can eliminate this method and call them directly. + NSString *keyAsString = [NSString stringWithCharacters:&functionKey length:1]; + return [NSEvent keyEventWithType:NSKeyDown + location:NSZeroPoint + modifierFlags:0 + timestamp:0 + windowNumber:0 + context:nil + characters:keyAsString + charactersIgnoringModifiers:keyAsString + isARepeat:NO + keyCode:0]; +} + +- (void)_lookUpInDictionaryFromMenu:(id)sender +{ + // This method is used by WebKit's context menu item. Here we map to the method that + // PDFView uses. Since the PDFView method isn't API, and isn't available on all versions + // of PDFKit, we use performSelector after a respondsToSelector check, rather than calling it directly. + if ([self _canLookUpInDictionary]) + [PDFSubview performSelector:@selector(_searchInDictionary:) withObject:sender]; +} + +- (NSMutableArray *)_menuItemsFromPDFKitForEvent:(NSEvent *)theEvent +{ + NSMutableArray *copiedItems = [NSMutableArray array]; + NSDictionary *actionsToTags = [[NSDictionary alloc] initWithObjectsAndKeys: + [NSNumber numberWithInt:WebMenuItemPDFActualSize], NSStringFromSelector(@selector(_setActualSize:)), + [NSNumber numberWithInt:WebMenuItemPDFZoomIn], NSStringFromSelector(@selector(zoomIn:)), + [NSNumber numberWithInt:WebMenuItemPDFZoomOut], NSStringFromSelector(@selector(zoomOut:)), + [NSNumber numberWithInt:WebMenuItemPDFAutoSize], NSStringFromSelector(@selector(_setAutoSize:)), + [NSNumber numberWithInt:WebMenuItemPDFSinglePage], NSStringFromSelector(@selector(_setSinglePage:)), + [NSNumber numberWithInt:WebMenuItemPDFSinglePageScrolling], NSStringFromSelector(@selector(_setSinglePageScrolling:)), + [NSNumber numberWithInt:WebMenuItemPDFFacingPages], NSStringFromSelector(@selector(_setDoublePage:)), + [NSNumber numberWithInt:WebMenuItemPDFFacingPagesScrolling], NSStringFromSelector(@selector(_setDoublePageScrolling:)), + [NSNumber numberWithInt:WebMenuItemPDFContinuous], NSStringFromSelector(@selector(_toggleContinuous:)), + [NSNumber numberWithInt:WebMenuItemPDFNextPage], NSStringFromSelector(@selector(goToNextPage:)), + [NSNumber numberWithInt:WebMenuItemPDFPreviousPage], NSStringFromSelector(@selector(goToPreviousPage:)), + nil]; + + // Leave these menu items out, since WebKit inserts equivalent ones. Note that we leave out PDFKit's "Look Up in Dictionary" + // item here because WebKit already includes an item with the same title and purpose. We map WebKit's to PDFKit's + // "Look Up in Dictionary" via the implementation of -[WebPDFView _lookUpInDictionaryFromMenu:]. + NSSet *unwantedActions = [[NSSet alloc] initWithObjects: + NSStringFromSelector(@selector(_searchInSpotlight:)), + NSStringFromSelector(@selector(_searchInGoogle:)), + NSStringFromSelector(@selector(_searchInDictionary:)), + NSStringFromSelector(@selector(copy:)), + nil]; + + NSEnumerator *e = [[[PDFSubview menuForEvent:theEvent] itemArray] objectEnumerator]; + NSMenuItem *item; + while ((item = [e nextObject]) != nil) { + + NSString *actionString = NSStringFromSelector([item action]); + + if ([unwantedActions containsObject:actionString]) + continue; + + // Copy items since a menu item can be in only one menu at a time, and we don't + // want to modify the original menu supplied by PDFKit. + NSMenuItem *itemCopy = [item copy]; + [copiedItems addObject:itemCopy]; + + // Include all of PDFKit's separators for now. At the end we'll remove any ones that were made + // useless by removing PDFKit's menu items. + if ([itemCopy isSeparatorItem]) + continue; + + NSNumber *tagNumber = [actionsToTags objectForKey:actionString]; + + int tag; + if (tagNumber != nil) + tag = [tagNumber intValue]; + else { + // This should happen only if PDFKit updates behind WebKit's back. It's non-ideal because clients that only include tags + // that they recognize (like Safari) won't get these PDFKit additions until WebKit is updated to match. + tag = WebMenuItemTagOther; + LOG_ERROR("no WebKit menu item tag found for PDF context menu item action \"%@\", using WebMenuItemTagOther", actionString); + } + + if ([itemCopy tag] == 0) { + [itemCopy setTag:tag]; + if ([itemCopy target] == PDFSubview) { + // Note that updating the defaults is cheap because it catches redundant settings, so installing + // the proxy for actions that don't impact the defaults is OK + [itemCopy setTarget:PDFSubviewProxy]; + } + } else + LOG_ERROR("PDF context menu item %@ came with tag %d, so no WebKit tag was applied. This could mean that the item doesn't appear in clients such as Safari.", [itemCopy title], [itemCopy tag]); + } + + [actionsToTags release]; + [unwantedActions release]; + + // Since we might have removed elements supplied by PDFKit, and we want to minimize our hardwired + // knowledge of the order and arrangement of PDFKit's menu items, we need to remove any bogus + // separators that were left behind. + [copiedItems _webkit_removeUselessMenuItemSeparators]; + + return copiedItems; +} + +- (PDFSelection *)_nextMatchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag fromSelection:(PDFSelection *)initialSelection startInSelection:(BOOL)startInSelection +{ + if (![string length]) + return nil; + + int options = 0; + if (!forward) + options |= NSBackwardsSearch; + + if (!caseFlag) + options |= NSCaseInsensitiveSearch; + + PDFDocument *document = [PDFSubview document]; + + PDFSelection *selectionForInitialSearch = [initialSelection copy]; + if (startInSelection) { + // Initially we want to include the selected text in the search. PDFDocument's API always searches from just + // past the passed-in selection, so we need to pass a selection that's modified appropriately. + // FIXME 4182863: Ideally we'd use a zero-length selection at the edge of the current selection, but zero-length + // selections don't work in PDFDocument. So instead we make a one-length selection just before or after the + // current selection, which works for our purposes even when the current selection is at an edge of the + // document. + int initialSelectionLength = [[initialSelection string] length]; + if (forward) { + [selectionForInitialSearch extendSelectionAtStart:1]; + [selectionForInitialSearch extendSelectionAtEnd:-initialSelectionLength]; + } else { + [selectionForInitialSearch extendSelectionAtEnd:1]; + [selectionForInitialSearch extendSelectionAtStart:-initialSelectionLength]; + } + } + PDFSelection *foundSelection = [document findString:string fromSelection:selectionForInitialSearch withOptions:options]; + [selectionForInitialSearch release]; + + // If we first searched in the selection, and we found the selection, search again from just past the selection + if (startInSelection && _PDFSelectionsAreEqual(foundSelection, initialSelection)) + foundSelection = [document findString:string fromSelection:initialSelection withOptions:options]; + + if (!foundSelection && wrapFlag) + foundSelection = [document findString:string fromSelection:nil withOptions:options]; + + return foundSelection; +} + +- (void)_openWithFinder:(id)sender +{ + // We don't want to write the file until we have a document to write (see 4892525). + if (![PDFSubview document]) { + NSBeep(); + return; + } + + NSString *opath = [self _path]; + + if (opath) { + if (!written) { + // Create a PDF file with the minimal permissions (only accessible to the current user, see 4145714) + NSNumber *permissions = [[NSNumber alloc] initWithInt:S_IRUSR]; + NSDictionary *fileAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:permissions, NSFilePosixPermissions, nil]; + [permissions release]; + + [[NSFileManager defaultManager] createFileAtPath:opath contents:[dataSource data] attributes:fileAttributes]; + + [fileAttributes release]; + written = YES; + } + + if (![[NSWorkspace sharedWorkspace] openFile:opath]) { + // NSWorkspace couldn't open file. Do we need an alert + // here? We ignore the error elsewhere. + } + } +} + +- (NSString *)_path +{ + // Generate path once. + if (path) + return path; + + NSString *filename = [[dataSource response] suggestedFilename]; + NSFileManager *manager = [NSFileManager defaultManager]; + NSString *temporaryPDFDirectoryPath = [self _temporaryPDFDirectoryPath]; + + if (!temporaryPDFDirectoryPath) { + // This should never happen; if it does we'll fail silently on non-debug builds. + ASSERT_NOT_REACHED(); + return nil; + } + + path = [temporaryPDFDirectoryPath stringByAppendingPathComponent:filename]; + if ([manager fileExistsAtPath:path]) { + NSString *pathTemplatePrefix = [temporaryPDFDirectoryPath stringByAppendingPathComponent:@"XXXXXX-"]; + NSString *pathTemplate = [pathTemplatePrefix stringByAppendingString:filename]; + // fileSystemRepresentation returns a const char *; copy it into a char * so we can modify it safely + char *cPath = strdup([pathTemplate fileSystemRepresentation]); + int fd = mkstemps(cPath, strlen(cPath) - strlen([pathTemplatePrefix fileSystemRepresentation]) + 1); + if (fd < 0) { + // Couldn't create a temporary file! Should never happen; if it does we'll fail silently on non-debug builds. + ASSERT_NOT_REACHED(); + path = nil; + } else { + close(fd); + path = [manager stringWithFileSystemRepresentation:cPath length:strlen(cPath)]; + } + free(cPath); + } + + [path retain]; + + return path; +} + +- (void)_PDFDocumentViewMightHaveScrolled:(NSNotification *)notification +{ + NSClipView *clipView = [self _clipViewForPDFDocumentView]; + ASSERT([notification object] == clipView); + + NSPoint scrollPosition = [clipView bounds].origin; + if (NSEqualPoints(scrollPosition, lastScrollPosition)) + return; + + lastScrollPosition = scrollPosition; + WebView *webView = [self _webView]; + [[webView _UIDelegateForwarder] webView:webView didScrollDocumentInFrameView:[[dataSource webFrame] frameView]]; +} + +- (PDFView *)_PDFSubview +{ + return PDFSubview; +} + +- (BOOL)_pointIsInSelection:(NSPoint)point +{ + PDFPage *page = [PDFSubview pageForPoint:point nearest:NO]; + if (!page) + return NO; + + NSRect selectionRect = [PDFSubview convertRect:[[PDFSubview currentSelection] boundsForPage:page] fromPage:page]; + + return NSPointInRect(point, selectionRect); +} + +- (void)_scaleOrDisplayModeOrPageChanged:(NSNotification *)notification +{ + ASSERT([notification object] == PDFSubview); + if (!_ignoreScaleAndDisplayModeAndPageNotifications) { + [self _updatePreferencesSoon]; + // Notify UI delegate that the entire page has been redrawn, since (unlike for WebHTMLView) + // we can't hook into the drawing mechanism itself. This fixes 5337529. + WebView *webView = [self _webView]; + [[webView _UIDelegateForwarder] webView:webView didDrawRect:[webView bounds]]; + } +} + +- (NSAttributedString *)_scaledAttributedString:(NSAttributedString *)unscaledAttributedString +{ + if (!unscaledAttributedString) + return nil; + + float scaleFactor = [PDFSubview scaleFactor]; + if (scaleFactor == 1.0) + return unscaledAttributedString; + + NSMutableAttributedString *result = [[unscaledAttributedString mutableCopy] autorelease]; + unsigned int length = [result length]; + NSRange effectiveRange = NSMakeRange(0,0); + + [result beginEditing]; + while (NSMaxRange(effectiveRange) < length) { + NSFont *unscaledFont = [result attribute:NSFontAttributeName atIndex:NSMaxRange(effectiveRange) effectiveRange:&effectiveRange]; + + if (!unscaledFont) { + // FIXME: We can't scale the font if we don't know what it is. We should always know what it is, + // but sometimes don't due to PDFKit issue 5089411. When that's addressed, we can remove this + // early continue. + LOG_ERROR("no font attribute found in range %@ for attributed string \"%@\" on page %@ (see radar 5089411)", NSStringFromRange(effectiveRange), result, [[dataSource request] URL]); + continue; + } + + NSFont *scaledFont = [NSFont fontWithName:[unscaledFont fontName] size:[unscaledFont pointSize]*scaleFactor]; + [result addAttribute:NSFontAttributeName value:scaledFont range:effectiveRange]; + } + [result endEditing]; + + return result; +} + +- (void)_setTextMatches:(NSArray *)array +{ + [array retain]; + [textMatches release]; + textMatches = array; +} + +- (NSString *)_temporaryPDFDirectoryPath +{ + // Returns nil if the temporary PDF directory didn't exist and couldn't be created + + static NSString *_temporaryPDFDirectoryPath = nil; + + if (!_temporaryPDFDirectoryPath) { + NSString *temporaryDirectoryTemplate = [NSTemporaryDirectory() stringByAppendingPathComponent:@"WebKitPDFs-XXXXXX"]; + char *cTemplate = strdup([temporaryDirectoryTemplate fileSystemRepresentation]); + + if (!mkdtemp(cTemplate)) { + // This should never happen; if it does we'll fail silently on non-debug builds. + ASSERT_NOT_REACHED(); + } else { + // cTemplate has now been modified to be the just-created directory name. This directory has 700 permissions, + // so only the current user can add to it or view its contents. + _temporaryPDFDirectoryPath = [[[NSFileManager defaultManager] stringWithFileSystemRepresentation:cTemplate length:strlen(cTemplate)] retain]; + } + + free(cTemplate); + } + + return _temporaryPDFDirectoryPath; +} + +- (void)_trackFirstResponder +{ + ASSERT([self window]); + BOOL newFirstResponderIsPDFDocumentView = [[self window] firstResponder] == [PDFSubview documentView]; + if (newFirstResponderIsPDFDocumentView == firstResponderIsPDFDocumentView) + return; + + // This next clause is the entire purpose of _trackFirstResponder. In other WebDocument + // view classes this is done in a resignFirstResponder override, but in this case the + // first responder view is a PDFKit class that we can't subclass. + if (newFirstResponderIsPDFDocumentView && ![[dataSource _webView] maintainsInactiveSelection]) + [self deselectAll]; + + firstResponderIsPDFDocumentView = newFirstResponderIsPDFDocumentView; +} + +- (void)_updatePreferences:(WebPreferences *)prefs +{ + float scaleFactor = [PDFSubview autoScales] ? 0.0f : [PDFSubview scaleFactor]; + [prefs setPDFScaleFactor:scaleFactor]; + [prefs setPDFDisplayMode:[PDFSubview displayMode]]; + _willUpdatePreferencesSoon = NO; + [prefs release]; + [self release]; +} + +- (void)_updatePreferencesSoon +{ + // Consolidate calls; due to the PDFPrefUpdatingProxy method, this can be called multiple times with a single user action + // such as showing the context menu. + if (_willUpdatePreferencesSoon) + return; + + WebPreferences *prefs = [[dataSource _webView] preferences]; + + [self retain]; + [prefs retain]; + [self performSelector:@selector(_updatePreferences:) withObject:prefs afterDelay:0]; + _willUpdatePreferencesSoon = YES; +} + +- (NSSet *)_visiblePDFPages +{ + // Returns the set of pages that are at least partly visible, used to avoid processing non-visible pages + PDFDocument *pdfDocument = [PDFSubview document]; + if (!pdfDocument) + return nil; + + NSRect pdfViewBounds = [PDFSubview bounds]; + PDFPage *topLeftPage = [PDFSubview pageForPoint:NSMakePoint(NSMinX(pdfViewBounds), NSMaxY(pdfViewBounds)) nearest:YES]; + PDFPage *bottomRightPage = [PDFSubview pageForPoint:NSMakePoint(NSMaxX(pdfViewBounds), NSMinY(pdfViewBounds)) nearest:YES]; + + // only page-free documents should return nil for either of these two since we passed YES for nearest: + if (!topLeftPage) { + ASSERT(!bottomRightPage); + return nil; + } + + NSUInteger firstVisiblePageIndex = [pdfDocument indexForPage:topLeftPage]; + NSUInteger lastVisiblePageIndex = [pdfDocument indexForPage:bottomRightPage]; + + if (firstVisiblePageIndex > lastVisiblePageIndex) { + NSUInteger swap = firstVisiblePageIndex; + firstVisiblePageIndex = lastVisiblePageIndex; + lastVisiblePageIndex = swap; + } + + NSMutableSet *result = [NSMutableSet set]; + NSUInteger pageIndex; + for (pageIndex = firstVisiblePageIndex; pageIndex <= lastVisiblePageIndex; ++pageIndex) + [result addObject:[pdfDocument pageAtIndex:pageIndex]]; + + return result; +} + +@end + +@implementation PDFPrefUpdatingProxy + +- (id)initWithView:(WebPDFView *)aView +{ + // No [super init], since we inherit from NSProxy + view = aView; + return self; +} + +- (void)forwardInvocation:(NSInvocation *)invocation +{ + [invocation invokeWithTarget:[view _PDFSubview]]; + [view _updatePreferencesSoon]; +} + +- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel +{ + return [[view _PDFSubview] methodSignatureForSelector:sel]; +} + +@end diff --git a/Source/WebKit/mac/WebView/WebPolicyDelegate.h b/Source/WebKit/mac/WebView/WebPolicyDelegate.h new file mode 100644 index 0000000..01d0843 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebPolicyDelegate.h @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2007 Apple Inc. All rights reserved. + * + * 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 <Cocoa/Cocoa.h> + +@class NSError; +@class NSURLResponse; +@class NSURLRequest; +@class WebView; +@class WebFrame; +@class WebPolicyPrivate; + +/*! + @enum WebNavigationType + @abstract The type of action that triggered a possible navigation. + @constant WebNavigationTypeLinkClicked A link with an href was clicked. + @constant WebNavigationTypeFormSubmitted A form was submitted. + @constant WebNavigationTypeBackForward The user chose back or forward. + @constant WebNavigationTypeReload The User hit the reload button. + @constant WebNavigationTypeFormResubmitted A form was resubmitted (by virtue of doing back, forward or reload). + @constant WebNavigationTypeOther Navigation is taking place for some other reason. +*/ + +typedef enum { + WebNavigationTypeLinkClicked, + WebNavigationTypeFormSubmitted, + WebNavigationTypeBackForward, + WebNavigationTypeReload, + WebNavigationTypeFormResubmitted, + WebNavigationTypeOther +} WebNavigationType; + +extern NSString *WebActionNavigationTypeKey; // NSNumber (WebNavigationType) +extern NSString *WebActionElementKey; // NSDictionary of element info +extern NSString *WebActionButtonKey; // NSNumber (0 for left button, 1 for middle button, 2 for right button) +extern NSString *WebActionModifierFlagsKey; // NSNumber (unsigned) +extern NSString *WebActionOriginalURLKey; // NSURL + +/*! + @protocol WebPolicyDecisionListener + @discussion This protocol is used to call back with the results of a + policy decision. This provides the ability to make these decisions + asyncrhonously, which means the decision can be made by prompting + with a sheet, for example. +*/ + +@protocol WebPolicyDecisionListener <NSObject> + +/*! + @method use + @abstract Use the resource + @discussion If there remain more policy decisions to be made, then + the next policy delegate method gets to decide. This will be + either the next navigation policy delegate if there is a redirect, + or the content policy delegate. If there are no more policy + decisions to be made, the resource will be displayed inline if + possible. If there is no view available to display the resource + inline, then unableToImplementPolicyWithError:frame: will be + called with an appropriate error. + + <p>If a new window is going to be created for this navigation as a + result of frame targetting, then it will be created once you call + this method. +*/ +- (void)use; +/*! + @method download + @abstract Download the resource instead of displaying it. + @discussion This method is more than just a convenience because it + allows an in-progress navigation to be converted to a download + based on content type, without having to stop and restart the + load. +*/ +- (void)download; + +/*! + @method ignore + @abstract Do nothing (but the client may choose to handle the request itself) + @discussion A policy of ignore prevents WebKit from doing anything + further with the load, however, the client is still free to handle + the request in some other way, such as opening a new window, + opening a new window behind the current one, opening the URL in an + external app, revealing the location in Finder if a file URL, etc. +*/ +- (void)ignore; + +@end + + +/*! + @category WebPolicyDelegate + @discussion While loading a URL, WebKit asks the WebPolicyDelegate for + policies that determine the action of what to do with the URL or the data that + the URL represents. Typically, the policy handler methods are called in this order: + + decidePolicyForNewWindowAction:request:newFrameName:decisionListener: (at most once)<BR> + decidePolicyForNavigationAction:request:frame:decisionListener: (zero or more times)<BR> + decidePolicyForMIMEType:request:frame: (zero or more times)<BR> + + New window policy is always checked. Navigation policy is checked + for the initial load and every redirect unless blocked by an + earlier policy. Content policy is checked once the content type is + known, unless an earlier policy prevented it. + + In rare cases, content policy might be checked more than + once. This occurs when loading a "multipart/x-mixed-replace" + document, also known as "server push". In this case, multiple + documents come in one navigation, with each replacing the last. In + this case, conent policy will be checked for each one. +*/ +@interface NSObject (WebPolicyDelegate) + +/*! + @method webView:decidePolicyForNavigationAction:request:frame:decisionListener: + @abstract This method is called to decide what to do with a proposed navigation. + @param actionInformation Dictionary that describes the action that triggered this navigation. + @param request The request for the proposed navigation + @param frame The WebFrame in which the navigation is happening + @param listener The object to call when the decision is made + @discussion This method will be called before loading starts, and + on every redirect. +*/ +- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation + request:(NSURLRequest *)request + frame:(WebFrame *)frame + decisionListener:(id<WebPolicyDecisionListener>)listener; + +/*! + @method webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener: + @discussion This method is called to decide what to do with an targetted nagivation that would open a new window. + @param actionInformation Dictionary that describes the action that triggered this navigation. + @param request The request for the proposed navigation + @param frame The frame in which the navigation is taking place + @param listener The object to call when the decision is made + @discussion This method is provided so that modified clicks on a targetted link which + opens a new frame can prevent the new window from being opened if they decide to + do something else, like download or present the new frame in a specialized way. + + <p>If this method picks a policy of Use, the new window will be + opened, and decidePolicyForNavigationAction:request:frame:decisionListner: + will be called with a WebNavigationType of WebNavigationTypeOther + in its action. This is to avoid possible confusion about the modifiers. +*/ +- (void)webView:(WebView *)webView decidePolicyForNewWindowAction:(NSDictionary *)actionInformation + request:(NSURLRequest *)request + newFrameName:(NSString *)frameName + decisionListener:(id<WebPolicyDecisionListener>)listener; + +/*! + @method webView:decidePolicyForMIMEType:request:frame: + @discussion Returns the policy for content which has been partially loaded. + Sent after webView:didStartProvisionalLoadForFrame: is sent on the WebFrameLoadDelegate. + @param type MIME type for the resource. + @param request A NSURLRequest for the partially loaded content. + @param frame The frame which is loading the URL. + @param listener The object to call when the decision is made +*/ +- (void)webView:(WebView *)webView decidePolicyForMIMEType:(NSString *)type + request:(NSURLRequest *)request + frame:(WebFrame *)frame + decisionListener:(id<WebPolicyDecisionListener>)listener; + +/*! + @method webView:unableToImplementPolicy:error:forURL:inFrame: + @discussion Called when a WebPolicy could not be implemented. It is up to the client to display appropriate feedback. + @param policy The policy that could not be implemented. + @param error The error that caused the policy to not be implemented. + @param URL The URL of the resource for which a particular action was requested but failed. + @param frame The frame in which the policy could not be implemented. +*/ +- (void)webView:(WebView *)webView unableToImplementPolicyWithError:(NSError *)error frame:(WebFrame *)frame; + +@end diff --git a/Source/WebKit/mac/WebView/WebPolicyDelegate.mm b/Source/WebKit/mac/WebView/WebPolicyDelegate.mm new file mode 100644 index 0000000..f2df744 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebPolicyDelegate.mm @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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 "WebPolicyDelegatePrivate.h" + +#import <WebCore/FrameLoaderTypes.h> +#import <objc/objc-runtime.h> + +using namespace WebCore; + +NSString *WebActionButtonKey = @"WebActionButtonKey"; +NSString *WebActionElementKey = @"WebActionElementKey"; +NSString *WebActionFormKey = @"WebActionFormKey"; +NSString *WebActionModifierFlagsKey = @"WebActionModifierFlagsKey"; +NSString *WebActionNavigationTypeKey = @"WebActionNavigationTypeKey"; +NSString *WebActionOriginalURLKey = @"WebActionOriginalURLKey"; + +@interface WebPolicyDecisionListenerPrivate : NSObject +{ +@public + id target; + SEL action; +} + +- (id)initWithTarget:(id)target action:(SEL)action; + +@end + +@implementation WebPolicyDecisionListenerPrivate + +- (id)initWithTarget:(id)t action:(SEL)a +{ + self = [super init]; + if (!self) + return nil; + target = [t retain]; + action = a; + return self; +} + +- (void)dealloc +{ + [target release]; + [super dealloc]; +} + +@end + +@implementation WebPolicyDecisionListener + +- (id)_initWithTarget:(id)target action:(SEL)action +{ + self = [super init]; + if (!self) + return nil; + _private = [[WebPolicyDecisionListenerPrivate alloc] initWithTarget:target action:action]; + return self; +} + +-(void)dealloc +{ + [_private release]; + [super dealloc]; +} + +- (void)_usePolicy:(PolicyAction)policy +{ + if (_private->target) + ((void (*)(id, SEL, PolicyAction))objc_msgSend)(_private->target, _private->action, policy); +} + +- (void)_invalidate +{ + id target = _private->target; + _private->target = nil; + [target release]; +} + +// WebPolicyDecisionListener implementation + +- (void)use +{ + [self _usePolicy:PolicyUse]; +} + +- (void)ignore +{ + [self _usePolicy:PolicyIgnore]; +} + +- (void)download +{ + [self _usePolicy:PolicyDownload]; +} + +@end diff --git a/Source/WebKit/mac/WebView/WebPolicyDelegatePrivate.h b/Source/WebKit/mac/WebView/WebPolicyDelegatePrivate.h new file mode 100644 index 0000000..45f8f45 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebPolicyDelegatePrivate.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005, 2008 Apple Inc. All rights reserved. + * + * 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 <WebKit/WebPolicyDelegate.h> + +@class WebHistoryItem; +@class WebPolicyDecisionListenerPrivate; + +extern NSString *WebActionFormKey; // HTMLFormElement + +typedef enum { + WebNavigationTypePlugInRequest = WebNavigationTypeOther + 1 +} WebExtraNavigationType; + +@interface WebPolicyDecisionListener : NSObject <WebPolicyDecisionListener> +{ +@private + WebPolicyDecisionListenerPrivate *_private; +} +- (id)_initWithTarget:(id)target action:(SEL)action; +- (void)_invalidate; +@end + +@interface NSObject (WebPolicyDelegatePrivate) +// Needed for <rdar://problem/3951283> can view pages from the back/forward cache that should be disallowed by Parental Controls +- (BOOL)webView:(WebView *)webView shouldGoToHistoryItem:(WebHistoryItem *)item; +@end diff --git a/Source/WebKit/mac/WebView/WebPreferenceKeysPrivate.h b/Source/WebKit/mac/WebView/WebPreferenceKeysPrivate.h new file mode 100644 index 0000000..93f83fc --- /dev/null +++ b/Source/WebKit/mac/WebView/WebPreferenceKeysPrivate.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * 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. + */ + +// These are private because callers should be using the cover methods. They are in +// a Private (as opposed to Internal) header file because Safari uses some of them +// for managed preferences. +#define WebKitLogLevelPreferenceKey @"WebKitLogLevel" +#define WebKitStandardFontPreferenceKey @"WebKitStandardFont" +#define WebKitFixedFontPreferenceKey @"WebKitFixedFont" +#define WebKitSerifFontPreferenceKey @"WebKitSerifFont" +#define WebKitSansSerifFontPreferenceKey @"WebKitSansSerifFont" +#define WebKitCursiveFontPreferenceKey @"WebKitCursiveFont" +#define WebKitFantasyFontPreferenceKey @"WebKitFantasyFont" +#define WebKitMinimumFontSizePreferenceKey @"WebKitMinimumFontSize" +#define WebKitMinimumLogicalFontSizePreferenceKey @"WebKitMinimumLogicalFontSize" +#define WebKitDefaultFontSizePreferenceKey @"WebKitDefaultFontSize" +#define WebKitDefaultFixedFontSizePreferenceKey @"WebKitDefaultFixedFontSize" +#define WebKitDefaultTextEncodingNamePreferenceKey @"WebKitDefaultTextEncodingName" +#define WebKitUsesEncodingDetectorPreferenceKey @"WebKitUsesEncodingDetector" +#define WebKitUserStyleSheetEnabledPreferenceKey @"WebKitUserStyleSheetEnabledPreferenceKey" +#define WebKitUserStyleSheetLocationPreferenceKey @"WebKitUserStyleSheetLocationPreferenceKey" +#define WebKitShouldPrintBackgroundsPreferenceKey @"WebKitShouldPrintBackgroundsPreferenceKey" +#define WebKitTextAreasAreResizablePreferenceKey @"WebKitTextAreasAreResizable" +#define WebKitShrinksStandaloneImagesToFitPreferenceKey @"WebKitShrinksStandaloneImagesToFit" +#define WebKitJavaEnabledPreferenceKey @"WebKitJavaEnabled" +#define WebKitJavaScriptEnabledPreferenceKey @"WebKitJavaScriptEnabled" +#define WebKitWebSecurityEnabledPreferenceKey @"WebKitWebSecurityEnabled" +#define WebKitAllowUniversalAccessFromFileURLsPreferenceKey @"WebKitAllowUniversalAccessFromFileURLs" +#define WebKitAllowFileAccessFromFileURLsPreferenceKey @"WebKitAllowFileAccessFromFileURLs" +#define WebKitJavaScriptCanOpenWindowsAutomaticallyPreferenceKey @"WebKitJavaScriptCanOpenWindowsAutomatically" +#define WebKitPluginsEnabledPreferenceKey @"WebKitPluginsEnabled" +#define WebKitDatabasesEnabledPreferenceKey @"WebKitDatabasesEnabledPreferenceKey" +#define WebKitLocalStorageEnabledPreferenceKey @"WebKitLocalStorageEnabledPreferenceKey" +#define WebKitExperimentalNotificationsEnabledPreferenceKey @"WebKitExperimentalNotificationsEnabledPreferenceKey" +#define WebKitAllowAnimatedImagesPreferenceKey @"WebKitAllowAnimatedImagesPreferenceKey" +#define WebKitAllowAnimatedImageLoopingPreferenceKey @"WebKitAllowAnimatedImageLoopingPreferenceKey" +#define WebKitDisplayImagesKey @"WebKitDisplayImagesKey" +#define WebKitBackForwardCacheExpirationIntervalKey @"WebKitBackForwardCacheExpirationIntervalKey" +#define WebKitTabToLinksPreferenceKey @"WebKitTabToLinksPreferenceKey" +#define WebKitPrivateBrowsingEnabledPreferenceKey @"WebKitPrivateBrowsingEnabled" +#define WebSmartInsertDeleteEnabled @"WebSmartInsertDeleteEnabled" +#define WebContinuousSpellCheckingEnabled @"WebContinuousSpellCheckingEnabled" +#define WebGrammarCheckingEnabled @"WebGrammarCheckingEnabled" +#define WebAutomaticQuoteSubstitutionEnabled @"WebAutomaticQuoteSubstitutionEnabled" +#define WebAutomaticLinkDetectionEnabled @"WebAutomaticLinkDetectionEnabled" +#define WebAutomaticDashSubstitutionEnabled @"WebAutomaticDashSubstitutionEnabled" +#define WebAutomaticTextReplacementEnabled @"WebAutomaticTextReplacementEnabled" +#define WebAutomaticSpellingCorrectionEnabled @"WebAutomaticSpellingCorrectionEnabled" +#define WebKitDOMPasteAllowedPreferenceKey @"WebKitDOMPasteAllowedPreferenceKey" +#define WebKitUsesPageCachePreferenceKey @"WebKitUsesPageCachePreferenceKey" +#define WebKitFTPDirectoryTemplatePath @"WebKitFTPDirectoryTemplatePath" +#define WebKitForceFTPDirectoryListings @"WebKitForceFTPDirectoryListings" +#define WebKitDeveloperExtrasEnabledPreferenceKey @"WebKitDeveloperExtrasEnabledPreferenceKey" +#define WebKitAuthorAndUserStylesEnabledPreferenceKey @"WebKitAuthorAndUserStylesEnabledPreferenceKey" +#define WebKitApplicationChromeModeEnabledPreferenceKey @"WebKitApplicationChromeModeEnabledPreferenceKey" +#define WebKitWebArchiveDebugModeEnabledPreferenceKey @"WebKitWebArchiveDebugModeEnabledPreferenceKey" +#define WebKitLocalFileContentSniffingEnabledPreferenceKey @"WebKitLocalFileContentSniffingEnabledPreferenceKey" +#define WebKitLocalStorageDatabasePathPreferenceKey @"WebKitLocalStorageDatabasePathPreferenceKey" +#define WebKitEnableFullDocumentTeardownPreferenceKey @"WebKitEnableFullDocumentTeardown" +#define WebKitOfflineWebApplicationCacheEnabledPreferenceKey @"WebKitOfflineWebApplicationCacheEnabled" +#define WebKitApplicationCacheTotalQuota @"WebKitApplicationCacheTotalQuota" +#define WebKitApplicationCacheDefaultOriginQuota @"WebKitApplicationCacheDefaultOriginQuota" +#define WebKitZoomsTextOnlyPreferenceKey @"WebKitZoomsTextOnly" +#define WebKitJavaScriptCanAccessClipboardPreferenceKey @"WebKitJavaScriptCanAccessClipboard" +#define WebKitXSSAuditorEnabledPreferenceKey @"WebKitXSSAuditorEnabled" +#define WebKitAcceleratedCompositingEnabledPreferenceKey @"WebKitAcceleratedCompositingEnabled" +#define WebKitShowDebugBordersPreferenceKey @"WebKitShowDebugBorders" +#define WebKitShowRepaintCounterPreferenceKey @"WebKitShowRepaintCounter" +#define WebKitWebGLEnabledPreferenceKey @"WebKitWebGLEnabled" +#define WebKitAccelerated2dCanvasEnabledPreferenceKey @"WebKitAccelerated2dCanvasEnabled" +#define WebKitPluginAllowedRunTimePreferenceKey @"WebKitPluginAllowedRunTime" +#define WebKitFrameFlatteningEnabledPreferenceKey @"WebKitFrameFlatteningEnabled" +#define WebKitSpatialNavigationEnabledPreferenceKey @"WebKitSpatialNavigationEnabled" +#define WebKitPaginateDuringLayoutEnabledPreferenceKey @"WebKitPaginateDuringLayoutEnabled" +#define WebKitDNSPrefetchingEnabledPreferenceKey @"WebKitDNSPrefetchingEnabled" +#define WebKitFullScreenEnabledPreferenceKey @"WebKitFullScreenEnabled" +#define WebKitAsynchronousSpellCheckingEnabledPreferenceKey @"WebKitAsynchronousSpellCheckingEnabled" +#define WebKitMemoryInfoEnabledPreferenceKey @"WebKitMemoryInfoEnabled" +#define WebKitHyperlinkAuditingEnabledPreferenceKey @"WebKitHyperlinkAuditingEnabled" + +// These are private both because callers should be using the cover methods and because the +// cover methods themselves are private. +#define WebKitRespectStandardStyleKeyEquivalentsPreferenceKey @"WebKitRespectStandardStyleKeyEquivalents" +#define WebKitShowsURLsInToolTipsPreferenceKey @"WebKitShowsURLsInToolTips" +#define WebKitPDFDisplayModePreferenceKey @"WebKitPDFDisplayMode" +#define WebKitPDFScaleFactorPreferenceKey @"WebKitPDFScaleFactor" +#define WebKitUseSiteSpecificSpoofingPreferenceKey @"WebKitUseSiteSpecificSpoofing" +#define WebKitEditableLinkBehaviorPreferenceKey @"WebKitEditableLinkBehavior" +#define WebKitCacheModelPreferenceKey @"WebKitCacheModelPreferenceKey" +#define WebKitTextDirectionSubmenuInclusionBehaviorPreferenceKey @"WebKitTextDirectionSubmenuInclusionBehaviorPreferenceKey" +#define WebKitEditingBehaviorPreferenceKey @"WebKitEditingBehavior" +#define WebKitUsePreHTML5ParserQuirksKey @"WebKitUsePreHTML5ParserQuirks" + +// CoreGraphics deferred updates are disabled if WebKitEnableCoalescedUpdatesPreferenceKey is set +// to NO, or has no value. For compatibility with Mac OS X 10.4.6, deferred updates are OFF by +// default. +#define WebKitEnableDeferredUpdatesPreferenceKey @"WebKitEnableDeferredUpdates" + +// For debugging only. Don't use these. +#define WebKitPageCacheSizePreferenceKey @"WebKitPageCacheSizePreferenceKey" +#define WebKitObjectCacheSizePreferenceKey @"WebKitObjectCacheSizePreferenceKey" +#define WebKitDebugFullPageZoomPreferenceKey @"WebKitDebugFullPageZoomPreferenceKey" diff --git a/Source/WebKit/mac/WebView/WebPreferences.h b/Source/WebKit/mac/WebView/WebPreferences.h new file mode 100644 index 0000000..1988acc --- /dev/null +++ b/Source/WebKit/mac/WebView/WebPreferences.h @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2003, 2004, 2005 Apple Computer, Inc. All rights reserved. + * + * 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 <Foundation/Foundation.h> + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 +#define WebNSUInteger unsigned int +#else +#define WebNSUInteger NSUInteger +#endif + +/*! +@enum WebCacheModel + +@abstract Specifies a usage model for a WebView, which WebKit will use to +determine its caching behavior. + +@constant WebCacheModelDocumentViewer Appropriate for a WebView displaying +a fixed document -- like a splash screen, a chat document, or a word processing +document -- with no UI for navigation. The WebView will behave like any other +view, releasing resources when they are no longer referenced. Remote resources, +if any, will be cached to disk. This is the most memory-efficient setting. + +Examples: iChat, Mail, TextMate, Growl. + +@constant WebCacheModelDocumentBrowser Appropriate for a WebView displaying +a browsable series of documents with a UI for navigating between them -- for +example, a reference materials browser or a website designer. The WebView will +cache a reasonable number of resources and previously viewed documents in +memory and/or on disk. + +Examples: Dictionary, Help Viewer, Coda. + +@constant WebCacheModelPrimaryWebBrowser Appropriate for a WebView in the +application that acts as the user's primary web browser. The WebView will cache +a very large number of resources and previously viewed documents in memory +and/or on disk. + +Examples: Safari, OmniWeb, Shiira. +*/ +enum { + WebCacheModelDocumentViewer = 0, + WebCacheModelDocumentBrowser = 1, + WebCacheModelPrimaryWebBrowser = 2 +}; +typedef WebNSUInteger WebCacheModel; + +@class WebPreferencesPrivate; + +extern NSString *WebPreferencesChangedNotification; + +/*! + @class WebPreferences +*/ +@interface WebPreferences: NSObject <NSCoding> +{ +@private + WebPreferencesPrivate *_private; +} + +/*! + @method standardPreferences +*/ ++ (WebPreferences *)standardPreferences; + +/*! + @method initWithIdentifier: + @param anIdentifier A string used to identify the WebPreferences. + @discussion WebViews can share instances of WebPreferences by using an instance of WebPreferences with + the same identifier. Typically, instance are not created directly. Instead you set the preferences + identifier on a WebView. The identifier is used as a prefix that is added to the user defaults keys + for the WebPreferences. + @result Returns a new instance of WebPreferences or a previously allocated instance with the same identifier. +*/ +- (id)initWithIdentifier:(NSString *)anIdentifier; + +/*! + @method identifier + @result Returns the identifier for this WebPreferences. +*/ +- (NSString *)identifier; + +/*! + @method standardFontFamily +*/ +- (NSString *)standardFontFamily; + +/*! + @method setStandardFontFamily: + @param family +*/ +- (void)setStandardFontFamily:(NSString *)family; + +/*! + @method fixedFontFamily +*/ +- (NSString *)fixedFontFamily; + +/*! + @method setFixedFontFamily: + @param family +*/ +- (void)setFixedFontFamily:(NSString *)family; + +/*! + @method serifFontFamily +*/ +- (NSString *)serifFontFamily; + +/*! + @method setSerifFontFamily: + @param family +*/ +- (void)setSerifFontFamily:(NSString *)family; + +/*! + @method sansSerifFontFamily +*/ +- (NSString *)sansSerifFontFamily; + +/*! + @method setSansSerifFontFamily: + @param family +*/ +- (void)setSansSerifFontFamily:(NSString *)family; + +/*! + @method cursiveFontFamily +*/ +- (NSString *)cursiveFontFamily; + +/*! + @method setCursiveFontFamily: + @param family +*/ +- (void)setCursiveFontFamily:(NSString *)family; + +/*! + @method fantasyFontFamily +*/ +- (NSString *)fantasyFontFamily; + +/*! + @method setFantasyFontFamily: + @param family +*/ +- (void)setFantasyFontFamily:(NSString *)family; + +/*! + @method defaultFontSize +*/ +- (int)defaultFontSize; + +/*! + @method setDefaultFontSize: + @param size +*/ +- (void)setDefaultFontSize:(int)size; + +/*! + @method defaultFixedFontSize +*/ +- (int)defaultFixedFontSize; + +/*! + @method setDefaultFixedFontSize: + @param size +*/ +- (void)setDefaultFixedFontSize:(int)size; + +/*! + @method minimumFontSize +*/ +- (int)minimumFontSize; + +/*! + @method setMinimumFontSize: + @param size +*/ +- (void)setMinimumFontSize:(int)size; + +/*! + @method minimumLogicalFontSize +*/ +- (int)minimumLogicalFontSize; + +/*! + @method setMinimumLogicalFontSize: + @param size +*/ +- (void)setMinimumLogicalFontSize:(int)size; + +/*! + @method defaultTextEncodingName +*/ +- (NSString *)defaultTextEncodingName; + +/*! + @method setDefaultTextEncodingName: + @param encoding +*/ +- (void)setDefaultTextEncodingName:(NSString *)encoding; + +/*! + @method userStyleSheetEnabled +*/ +- (BOOL)userStyleSheetEnabled; + +/*! + @method setUserStyleSheetEnabled: + @param flag +*/ +- (void)setUserStyleSheetEnabled:(BOOL)flag; + +/*! + @method userStyleSheetLocation + @discussion The location of the user style sheet. +*/ +- (NSURL *)userStyleSheetLocation; + +/*! + @method setUserStyleSheetLocation: + @param URL The location of the user style sheet. +*/ +- (void)setUserStyleSheetLocation:(NSURL *)URL; + +/*! + @method isJavaEnabled +*/ +- (BOOL)isJavaEnabled; + +/*! + @method setJavaEnabled: + @param flag +*/ +- (void)setJavaEnabled:(BOOL)flag; + +/*! + @method isJavaScriptEnabled +*/ +- (BOOL)isJavaScriptEnabled; + +/*! + @method setJavaScriptEnabled: + @param flag +*/ +- (void)setJavaScriptEnabled:(BOOL)flag; + +/*! + @method JavaScriptCanOpenWindowsAutomatically +*/ +- (BOOL)javaScriptCanOpenWindowsAutomatically; + +/*! + @method setJavaScriptCanOpenWindowsAutomatically: + @param flag +*/ +- (void)setJavaScriptCanOpenWindowsAutomatically:(BOOL)flag; + +/*! + @method arePlugInsEnabled +*/ +- (BOOL)arePlugInsEnabled; + +/*! + @method setPlugInsEnabled: + @param flag +*/ +- (void)setPlugInsEnabled:(BOOL)flag; + +/*! + @method allowAnimatedImages +*/ +- (BOOL)allowsAnimatedImages; + +/*! + @method setAllowAnimatedImages: + @param flag +*/ +- (void)setAllowsAnimatedImages:(BOOL)flag; + +/*! + @method allowAnimatedImageLooping +*/ +- (BOOL)allowsAnimatedImageLooping; + +/*! + @method setAllowAnimatedImageLooping: + @param flag +*/ +- (void)setAllowsAnimatedImageLooping: (BOOL)flag; + +/*! + @method setWillLoadImagesAutomatically: + @param flag +*/ +- (void)setLoadsImagesAutomatically: (BOOL)flag; + +/*! + @method willLoadImagesAutomatically +*/ +- (BOOL)loadsImagesAutomatically; + +/*! + @method setAutosaves: + @param flag + @discussion If autosave preferences is YES the settings represented by + WebPreferences will be stored in the user defaults database. +*/ +- (void)setAutosaves:(BOOL)flag; + +/*! + @method autosaves + @result The value of the autosave preferences flag. +*/ +- (BOOL)autosaves; + +/*! + @method setShouldPrintBackgrounds: + @param flag +*/ +- (void)setShouldPrintBackgrounds:(BOOL)flag; + +/*! + @method shouldPrintBackgrounds + @result The value of the shouldPrintBackgrounds preferences flag +*/ +- (BOOL)shouldPrintBackgrounds; + +/*! + @method setPrivateBrowsingEnabled: + @param flag + @abstract If private browsing is enabled, WebKit will not store information + about sites the user visits. + */ +- (void)setPrivateBrowsingEnabled:(BOOL)flag; + +/*! + @method privateBrowsingEnabled + */ +- (BOOL)privateBrowsingEnabled; + +/*! + @method setTabsToLinks: + @param flag + @abstract If tabsToLinks is YES, the tab key will focus links and form controls. + The option key temporarily reverses this preference. +*/ +- (void)setTabsToLinks:(BOOL)flag; + +/*! + @method tabsToLinks +*/ +- (BOOL)tabsToLinks; + +/*! + @method setUsesPageCache: + @abstract Sets whether the receiver's associated WebViews use the shared + page cache. + @param UsesPageCache Whether the receiver's associated WebViews use the + shared page cache. + @discussion Pages are cached as they are added to a WebBackForwardList, and + removed from the cache as they are removed from a WebBackForwardList. Because + the page cache is global, caching a page in one WebBackForwardList may cause + a page in another WebBackForwardList to be evicted from the cache. +*/ +- (void)setUsesPageCache:(BOOL)usesPageCache; + +/*! + @method usesPageCache + @abstract Returns whether the receiver should use the shared page cache. + @result Whether the receiver should use the shared page cache. + @discussion Pages are cached as they are added to a WebBackForwardList, and + removed from the cache as they are removed from a WebBackForwardList. Because + the page cache is global, caching a page in one WebBackForwardList may cause + a page in another WebBackForwardList to be evicted from the cache. +*/ +- (BOOL)usesPageCache; + +/*! +@method setCacheModel: + +@abstract Specifies a usage model for a WebView, which WebKit will use to +determine its caching behavior. + +@param cacheModel The WebView's usage model for WebKit. If necessary, WebKit +will prune its caches to match cacheModel. + +@discussion Research indicates that users tend to browse within clusters of +documents that hold resources in common, and to revisit previously visited +documents. WebKit and the frameworks below it include built-in caches that take +advantage of these patterns, substantially improving document load speed in +browsing situations. The WebKit cache model controls the behaviors of all of +these caches, including NSURLCache and the various WebCore caches. + +Applications with a browsing interface can improve document load speed +substantially by specifying WebCacheModelDocumentBrowser. Applications without +a browsing interface can reduce memory usage substantially by specifying +WebCacheModelDocumentViewer. + +If setCacheModel: is not called, WebKit will select a cache model automatically. +*/ +- (void)setCacheModel:(WebCacheModel)cacheModel; + +/*! +@method cacheModel: + +@abstract Returns the usage model according to which WebKit determines its +caching behavior. + +@result The usage model. +*/ +- (WebCacheModel)cacheModel; + +@end + +#undef WebNSUInteger diff --git a/Source/WebKit/mac/WebView/WebPreferences.mm b/Source/WebKit/mac/WebView/WebPreferences.mm new file mode 100644 index 0000000..7ce3bc4 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebPreferences.mm @@ -0,0 +1,1429 @@ +/* + * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved. + * (C) 2006 Graham Dennis (graham.dennis@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 "WebPreferencesPrivate.h" +#import "WebPreferenceKeysPrivate.h" + +#import "WebApplicationCache.h" +#import "WebKitLogging.h" +#import "WebKitNSStringExtras.h" +#import "WebKitSystemBits.h" +#import "WebKitSystemInterface.h" +#import "WebKitVersionChecks.h" +#import "WebNSDictionaryExtras.h" +#import "WebNSURLExtras.h" +#import <WebCore/ApplicationCacheStorage.h> + +NSString *WebPreferencesChangedNotification = @"WebPreferencesChangedNotification"; +NSString *WebPreferencesRemovedNotification = @"WebPreferencesRemovedNotification"; + +#define KEY(x) (_private->identifier ? [_private->identifier stringByAppendingString:(x)] : (x)) + +enum { WebPreferencesVersion = 1 }; + +static WebPreferences *_standardPreferences; +static NSMutableDictionary *webPreferencesInstances; + +static bool contains(const char* const array[], int count, const char* item) +{ + if (!item) + return false; + + for (int i = 0; i < count; i++) + if (!strcasecmp(array[i], item)) + return true; + return false; +} + +static WebCacheModel cacheModelForMainBundle(void) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + // Apps that probably need the small setting + static const char* const documentViewerIDs[] = { + "Microsoft/com.microsoft.Messenger", + "com.adiumX.adiumX", + "com.alientechnology.Proteus", + "com.apple.Dashcode", + "com.apple.iChat", + "com.barebones.bbedit", + "com.barebones.textwrangler", + "com.barebones.yojimbo", + "com.equinux.iSale4", + "com.growl.growlframework", + "com.intrarts.PandoraMan", + "com.karelia.Sandvox", + "com.macromates.textmate", + "com.realmacsoftware.rapidweaverpro", + "com.red-sweater.marsedit", + "com.yahoo.messenger3", + "de.codingmonkeys.SubEthaEdit", + "fi.karppinen.Pyro", + "info.colloquy", + "kungfoo.tv.ecto", + }; + + // Apps that probably need the medium setting + static const char* const documentBrowserIDs[] = { + "com.apple.Dictionary", + "com.apple.Xcode", + "com.apple.dashboard.client", + "com.apple.helpviewer", + "com.culturedcode.xyle", + "com.macrabbit.CSSEdit", + "com.panic.Coda", + "com.ranchero.NetNewsWire", + "com.thinkmac.NewsLife", + "org.xlife.NewsFire", + "uk.co.opencommunity.vienna2", + }; + + // Apps that probably need the large setting + static const char* const primaryWebBrowserIDs[] = { + "com.app4mac.KidsBrowser" + "com.app4mac.wKiosk", + "com.freeverse.bumpercar", + "com.omnigroup.OmniWeb5", + "com.sunrisebrowser.Sunrise", + "net.hmdt-web.Shiira", + }; + + WebCacheModel cacheModel; + + const char* bundleID = [[[NSBundle mainBundle] bundleIdentifier] UTF8String]; + if (contains(documentViewerIDs, sizeof(documentViewerIDs) / sizeof(documentViewerIDs[0]), bundleID)) + cacheModel = WebCacheModelDocumentViewer; + else if (contains(documentBrowserIDs, sizeof(documentBrowserIDs) / sizeof(documentBrowserIDs[0]), bundleID)) + cacheModel = WebCacheModelDocumentBrowser; + else if (contains(primaryWebBrowserIDs, sizeof(primaryWebBrowserIDs) / sizeof(primaryWebBrowserIDs[0]), bundleID)) + cacheModel = WebCacheModelPrimaryWebBrowser; + else { + bool isLegacyApp = !WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_CACHE_MODEL_API); + if (isLegacyApp) + cacheModel = WebCacheModelDocumentBrowser; // To avoid regressions in apps that depended on old WebKit's large cache. + else + cacheModel = WebCacheModelDocumentViewer; // To save memory. + } + + [pool drain]; + + return cacheModel; +} + +@interface WebPreferencesPrivate : NSObject +{ +@public + NSMutableDictionary *values; + NSString *identifier; + NSString *IBCreatorID; + BOOL autosaves; + BOOL automaticallyDetectsCacheModel; + unsigned numWebViews; +} +@end + +@implementation WebPreferencesPrivate +- (void)dealloc +{ + [values release]; + [identifier release]; + [IBCreatorID release]; + [super dealloc]; +} +@end + +@interface WebPreferences (WebInternal) ++ (NSString *)_concatenateKeyWithIBCreatorID:(NSString *)key; ++ (NSString *)_IBCreatorID; +@end + +@interface WebPreferences (WebForwardDeclarations) +// This pseudo-category is needed so these methods can be used from within other category implementations +// without being in the public header file. +- (BOOL)_boolValueForKey:(NSString *)key; +- (void)_setBoolValue:(BOOL)value forKey:(NSString *)key; +- (int)_integerValueForKey:(NSString *)key; +- (void)_setIntegerValue:(int)value forKey:(NSString *)key; +- (float)_floatValueForKey:(NSString *)key; +- (void)_setFloatValue:(float)value forKey:(NSString *)key; +- (void)_setLongLongValue:(long long)value forKey:(NSString *)key; +- (long long)_longLongValueForKey:(NSString *)key; +- (void)_setUnsignedLongLongValue:(unsigned long long)value forKey:(NSString *)key; +- (unsigned long long)_unsignedLongLongValueForKey:(NSString *)key; +@end + +@implementation WebPreferences + +- (id)init +{ + // Create fake identifier + static int instanceCount = 1; + NSString *fakeIdentifier; + + // At least ensure that identifier hasn't been already used. + fakeIdentifier = [NSString stringWithFormat:@"WebPreferences%d", instanceCount++]; + while ([[self class] _getInstanceForIdentifier:fakeIdentifier]){ + fakeIdentifier = [NSString stringWithFormat:@"WebPreferences%d", instanceCount++]; + } + + return [self initWithIdentifier:fakeIdentifier]; +} + +- (id)initWithIdentifier:(NSString *)anIdentifier +{ + self = [super init]; + if (!self) + return nil; + + _private = [[WebPreferencesPrivate alloc] init]; + _private->IBCreatorID = [[WebPreferences _IBCreatorID] retain]; + + WebPreferences *instance = [[self class] _getInstanceForIdentifier:anIdentifier]; + if (instance){ + [self release]; + return [instance retain]; + } + + _private->values = [[NSMutableDictionary alloc] init]; + _private->identifier = [anIdentifier copy]; + _private->automaticallyDetectsCacheModel = YES; + + [[self class] _setInstance:self forIdentifier:_private->identifier]; + + [self _postPreferencesChangesNotification]; + + return self; +} + +- (id)initWithCoder:(NSCoder *)decoder +{ + self = [super init]; + if (!self) + return nil; + + _private = [[WebPreferencesPrivate alloc] init]; + _private->IBCreatorID = [[WebPreferences _IBCreatorID] retain]; + _private->automaticallyDetectsCacheModel = YES; + + @try { + id identifier = nil; + id values = nil; + if ([decoder allowsKeyedCoding]) { + identifier = [decoder decodeObjectForKey:@"Identifier"]; + values = [decoder decodeObjectForKey:@"Values"]; + } else { + int version; + [decoder decodeValueOfObjCType:@encode(int) at:&version]; + if (version == 1) { + identifier = [decoder decodeObject]; + values = [decoder decodeObject]; + } + } + + if ([identifier isKindOfClass:[NSString class]]) + _private->identifier = [identifier copy]; + if ([values isKindOfClass:[NSDictionary class]]) + _private->values = [values mutableCopy]; // ensure dictionary is mutable + + LOG(Encoding, "Identifier = %@, Values = %@\n", _private->identifier, _private->values); + } @catch(id) { + [self release]; + return nil; + } + + // If we load a nib multiple times, or have instances in multiple + // nibs with the same name, the first guy up wins. + WebPreferences *instance = [[self class] _getInstanceForIdentifier:_private->identifier]; + if (instance) { + [self release]; + self = [instance retain]; + } else { + [[self class] _setInstance:self forIdentifier:_private->identifier]; + } + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)encoder +{ + if ([encoder allowsKeyedCoding]){ + [encoder encodeObject:_private->identifier forKey:@"Identifier"]; + [encoder encodeObject:_private->values forKey:@"Values"]; + LOG (Encoding, "Identifier = %@, Values = %@\n", _private->identifier, _private->values); + } + else { + int version = WebPreferencesVersion; + [encoder encodeValueOfObjCType:@encode(int) at:&version]; + [encoder encodeObject:_private->identifier]; + [encoder encodeObject:_private->values]; + } +} + ++ (WebPreferences *)standardPreferences +{ + if (_standardPreferences == nil) { + _standardPreferences = [[WebPreferences alloc] initWithIdentifier:nil]; + [_standardPreferences setAutosaves:YES]; + } + + return _standardPreferences; +} + +// if we ever have more than one WebPreferences object, this would move to init ++ (void)initialize +{ + NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: + @"Times", WebKitStandardFontPreferenceKey, + @"Courier", WebKitFixedFontPreferenceKey, + @"Times", WebKitSerifFontPreferenceKey, + @"Helvetica", WebKitSansSerifFontPreferenceKey, + @"Apple Chancery", WebKitCursiveFontPreferenceKey, + @"Papyrus", WebKitFantasyFontPreferenceKey, + @"0", WebKitMinimumFontSizePreferenceKey, + @"9", WebKitMinimumLogicalFontSizePreferenceKey, + @"16", WebKitDefaultFontSizePreferenceKey, + @"13", WebKitDefaultFixedFontSizePreferenceKey, + @"ISO-8859-1", WebKitDefaultTextEncodingNamePreferenceKey, + [NSNumber numberWithBool:NO], WebKitUsesEncodingDetectorPreferenceKey, + [NSNumber numberWithBool:NO], WebKitUserStyleSheetEnabledPreferenceKey, + @"", WebKitUserStyleSheetLocationPreferenceKey, + [NSNumber numberWithBool:NO], WebKitShouldPrintBackgroundsPreferenceKey, + [NSNumber numberWithBool:NO], WebKitTextAreasAreResizablePreferenceKey, + [NSNumber numberWithBool:NO], WebKitShrinksStandaloneImagesToFitPreferenceKey, + [NSNumber numberWithBool:YES], WebKitJavaEnabledPreferenceKey, + [NSNumber numberWithBool:YES], WebKitJavaScriptEnabledPreferenceKey, + [NSNumber numberWithBool:YES], WebKitWebSecurityEnabledPreferenceKey, + [NSNumber numberWithBool:YES], WebKitAllowUniversalAccessFromFileURLsPreferenceKey, + [NSNumber numberWithBool:YES], WebKitAllowFileAccessFromFileURLsPreferenceKey, + [NSNumber numberWithBool:YES], WebKitJavaScriptCanOpenWindowsAutomaticallyPreferenceKey, + [NSNumber numberWithBool:YES], WebKitPluginsEnabledPreferenceKey, + [NSNumber numberWithBool:YES], WebKitDatabasesEnabledPreferenceKey, + [NSNumber numberWithBool:YES], WebKitLocalStorageEnabledPreferenceKey, + [NSNumber numberWithBool:NO], WebKitExperimentalNotificationsEnabledPreferenceKey, + [NSNumber numberWithBool:YES], WebKitAllowAnimatedImagesPreferenceKey, + [NSNumber numberWithBool:YES], WebKitAllowAnimatedImageLoopingPreferenceKey, + [NSNumber numberWithBool:YES], WebKitDisplayImagesKey, + @"1800", WebKitBackForwardCacheExpirationIntervalKey, + [NSNumber numberWithBool:NO], WebKitTabToLinksPreferenceKey, + [NSNumber numberWithBool:NO], WebKitPrivateBrowsingEnabledPreferenceKey, + [NSNumber numberWithBool:NO], WebKitRespectStandardStyleKeyEquivalentsPreferenceKey, + [NSNumber numberWithBool:NO], WebKitShowsURLsInToolTipsPreferenceKey, + @"1", WebKitPDFDisplayModePreferenceKey, + @"0", WebKitPDFScaleFactorPreferenceKey, + @"0", WebKitUseSiteSpecificSpoofingPreferenceKey, + [NSNumber numberWithInt:WebKitEditableLinkDefaultBehavior], WebKitEditableLinkBehaviorPreferenceKey, + [NSNumber numberWithInt:WebKitEditingMacBehavior], WebKitEditingBehaviorPreferenceKey, +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + [NSNumber numberWithInt:WebTextDirectionSubmenuAutomaticallyIncluded], +#else + [NSNumber numberWithInt:WebTextDirectionSubmenuNeverIncluded], +#endif + WebKitTextDirectionSubmenuInclusionBehaviorPreferenceKey, + [NSNumber numberWithBool:NO], WebKitDOMPasteAllowedPreferenceKey, + [NSNumber numberWithBool:YES], WebKitUsesPageCachePreferenceKey, + [NSNumber numberWithInt:cacheModelForMainBundle()], WebKitCacheModelPreferenceKey, + [NSNumber numberWithBool:NO], WebKitDeveloperExtrasEnabledPreferenceKey, + [NSNumber numberWithBool:YES], WebKitAuthorAndUserStylesEnabledPreferenceKey, + [NSNumber numberWithBool:NO], WebKitApplicationChromeModeEnabledPreferenceKey, + [NSNumber numberWithBool:NO], WebKitWebArchiveDebugModeEnabledPreferenceKey, + [NSNumber numberWithBool:NO], WebKitLocalFileContentSniffingEnabledPreferenceKey, + [NSNumber numberWithBool:NO], WebKitOfflineWebApplicationCacheEnabledPreferenceKey, + [NSNumber numberWithBool:YES], WebKitZoomsTextOnlyPreferenceKey, + [NSNumber numberWithBool:NO], WebKitJavaScriptCanAccessClipboardPreferenceKey, + [NSNumber numberWithBool:YES], WebKitXSSAuditorEnabledPreferenceKey, + [NSNumber numberWithBool:YES], WebKitAcceleratedCompositingEnabledPreferenceKey, + [NSNumber numberWithBool:NO], WebKitShowDebugBordersPreferenceKey, + [NSNumber numberWithBool:NO], WebKitShowRepaintCounterPreferenceKey, + [NSNumber numberWithBool:NO], WebKitWebGLEnabledPreferenceKey, + [NSNumber numberWithBool:NO], WebKitAccelerated2dCanvasEnabledPreferenceKey, + [NSNumber numberWithUnsignedInt:4], WebKitPluginAllowedRunTimePreferenceKey, + [NSNumber numberWithBool:NO], WebKitFrameFlatteningEnabledPreferenceKey, + [NSNumber numberWithBool:NO], WebKitSpatialNavigationEnabledPreferenceKey, + [NSNumber numberWithBool:NO], WebKitDNSPrefetchingEnabledPreferenceKey, + [NSNumber numberWithBool:NO], WebKitFullScreenEnabledPreferenceKey, + [NSNumber numberWithBool:NO], WebKitAsynchronousSpellCheckingEnabledPreferenceKey, + [NSNumber numberWithBool:NO], WebKitMemoryInfoEnabledPreferenceKey, + [NSNumber numberWithBool:YES], WebKitHyperlinkAuditingEnabledPreferenceKey, + [NSNumber numberWithBool:NO], WebKitUsePreHTML5ParserQuirksKey, + [NSNumber numberWithLongLong:WebCore::ApplicationCacheStorage::noQuota()], WebKitApplicationCacheTotalQuota, + [NSNumber numberWithLongLong:WebCore::ApplicationCacheStorage::noQuota()], WebKitApplicationCacheDefaultOriginQuota, + nil]; + + // This value shouldn't ever change, which is assumed in the initialization of WebKitPDFDisplayModePreferenceKey above + ASSERT(kPDFDisplaySinglePageContinuous == 1); + [[NSUserDefaults standardUserDefaults] registerDefaults:dict]; +} + +- (void)dealloc +{ + [_private release]; + [super dealloc]; +} + +- (NSString *)identifier +{ + return _private->identifier; +} + +- (id)_valueForKey:(NSString *)key +{ + NSString *_key = KEY(key); + id o = [_private->values objectForKey:_key]; + if (o) + return o; + o = [[NSUserDefaults standardUserDefaults] objectForKey:_key]; + if (!o && key != _key) + o = [[NSUserDefaults standardUserDefaults] objectForKey:key]; + return o; +} + +- (NSString *)_stringValueForKey:(NSString *)key +{ + id s = [self _valueForKey:key]; + return [s isKindOfClass:[NSString class]] ? (NSString *)s : nil; +} + +- (void)_setStringValue:(NSString *)value forKey:(NSString *)key +{ + if ([[self _stringValueForKey:key] isEqualToString:value]) + return; + NSString *_key = KEY(key); + [_private->values setObject:value forKey:_key]; + if (_private->autosaves) + [[NSUserDefaults standardUserDefaults] setObject:value forKey:_key]; + [self _postPreferencesChangesNotification]; +} + +- (int)_integerValueForKey:(NSString *)key +{ + id o = [self _valueForKey:key]; + return [o respondsToSelector:@selector(intValue)] ? [o intValue] : 0; +} + +- (void)_setIntegerValue:(int)value forKey:(NSString *)key +{ + if ([self _integerValueForKey:key] == value) + return; + NSString *_key = KEY(key); + [_private->values _webkit_setInt:value forKey:_key]; + if (_private->autosaves) + [[NSUserDefaults standardUserDefaults] setInteger:value forKey:_key]; + [self _postPreferencesChangesNotification]; +} + +- (float)_floatValueForKey:(NSString *)key +{ + id o = [self _valueForKey:key]; + return [o respondsToSelector:@selector(floatValue)] ? [o floatValue] : 0.0f; +} + +- (void)_setFloatValue:(float)value forKey:(NSString *)key +{ + if ([self _floatValueForKey:key] == value) + return; + NSString *_key = KEY(key); + [_private->values _webkit_setFloat:value forKey:_key]; + if (_private->autosaves) + [[NSUserDefaults standardUserDefaults] setFloat:value forKey:_key]; + [self _postPreferencesChangesNotification]; +} + +- (BOOL)_boolValueForKey:(NSString *)key +{ + return [self _integerValueForKey:key] != 0; +} + +- (void)_setBoolValue:(BOOL)value forKey:(NSString *)key +{ + if ([self _boolValueForKey:key] == value) + return; + NSString *_key = KEY(key); + [_private->values _webkit_setBool:value forKey:_key]; + if (_private->autosaves) + [[NSUserDefaults standardUserDefaults] setBool:value forKey:_key]; + [self _postPreferencesChangesNotification]; +} + +- (long long)_longLongValueForKey:(NSString *)key +{ + id o = [self _valueForKey:key]; + return [o respondsToSelector:@selector(longLongValue)] ? [o longLongValue] : 0; +} + +- (void)_setLongLongValue:(long long)value forKey:(NSString *)key +{ + if ([self _longLongValueForKey:key] == value) + return; + NSString *_key = KEY(key); + [_private->values _webkit_setLongLong:value forKey:_key]; + if (_private->autosaves) + [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithLongLong:value] forKey:_key]; + [self _postPreferencesChangesNotification]; +} + +- (unsigned long long)_unsignedLongLongValueForKey:(NSString *)key +{ + id o = [self _valueForKey:key]; + return [o respondsToSelector:@selector(unsignedLongLongValue)] ? [o unsignedLongLongValue] : 0; +} + +- (void)_setUnsignedLongLongValue:(unsigned long long)value forKey:(NSString *)key +{ + if ([self _unsignedLongLongValueForKey:key] == value) + return; + NSString *_key = KEY(key); + [_private->values _webkit_setUnsignedLongLong:value forKey:_key]; + if (_private->autosaves) + [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithUnsignedLongLong:value] forKey:_key]; + [self _postPreferencesChangesNotification]; +} + +- (NSString *)standardFontFamily +{ + return [self _stringValueForKey: WebKitStandardFontPreferenceKey]; +} + +- (void)setStandardFontFamily:(NSString *)family +{ + [self _setStringValue: family forKey: WebKitStandardFontPreferenceKey]; +} + +- (NSString *)fixedFontFamily +{ + return [self _stringValueForKey: WebKitFixedFontPreferenceKey]; +} + +- (void)setFixedFontFamily:(NSString *)family +{ + [self _setStringValue: family forKey: WebKitFixedFontPreferenceKey]; +} + +- (NSString *)serifFontFamily +{ + return [self _stringValueForKey: WebKitSerifFontPreferenceKey]; +} + +- (void)setSerifFontFamily:(NSString *)family +{ + [self _setStringValue: family forKey: WebKitSerifFontPreferenceKey]; +} + +- (NSString *)sansSerifFontFamily +{ + return [self _stringValueForKey: WebKitSansSerifFontPreferenceKey]; +} + +- (void)setSansSerifFontFamily:(NSString *)family +{ + [self _setStringValue: family forKey: WebKitSansSerifFontPreferenceKey]; +} + +- (NSString *)cursiveFontFamily +{ + return [self _stringValueForKey: WebKitCursiveFontPreferenceKey]; +} + +- (void)setCursiveFontFamily:(NSString *)family +{ + [self _setStringValue: family forKey: WebKitCursiveFontPreferenceKey]; +} + +- (NSString *)fantasyFontFamily +{ + return [self _stringValueForKey: WebKitFantasyFontPreferenceKey]; +} + +- (void)setFantasyFontFamily:(NSString *)family +{ + [self _setStringValue: family forKey: WebKitFantasyFontPreferenceKey]; +} + +- (int)defaultFontSize +{ + return [self _integerValueForKey: WebKitDefaultFontSizePreferenceKey]; +} + +- (void)setDefaultFontSize:(int)size +{ + [self _setIntegerValue: size forKey: WebKitDefaultFontSizePreferenceKey]; +} + +- (int)defaultFixedFontSize +{ + return [self _integerValueForKey: WebKitDefaultFixedFontSizePreferenceKey]; +} + +- (void)setDefaultFixedFontSize:(int)size +{ + [self _setIntegerValue: size forKey: WebKitDefaultFixedFontSizePreferenceKey]; +} + +- (int)minimumFontSize +{ + return [self _integerValueForKey: WebKitMinimumFontSizePreferenceKey]; +} + +- (void)setMinimumFontSize:(int)size +{ + [self _setIntegerValue: size forKey: WebKitMinimumFontSizePreferenceKey]; +} + +- (int)minimumLogicalFontSize +{ + return [self _integerValueForKey: WebKitMinimumLogicalFontSizePreferenceKey]; +} + +- (void)setMinimumLogicalFontSize:(int)size +{ + [self _setIntegerValue: size forKey: WebKitMinimumLogicalFontSizePreferenceKey]; +} + +- (NSString *)defaultTextEncodingName +{ + return [self _stringValueForKey: WebKitDefaultTextEncodingNamePreferenceKey]; +} + +- (void)setDefaultTextEncodingName:(NSString *)encoding +{ + [self _setStringValue: encoding forKey: WebKitDefaultTextEncodingNamePreferenceKey]; +} + +- (BOOL)userStyleSheetEnabled +{ + return [self _boolValueForKey: WebKitUserStyleSheetEnabledPreferenceKey]; +} + +- (void)setUserStyleSheetEnabled:(BOOL)flag +{ + [self _setBoolValue: flag forKey: WebKitUserStyleSheetEnabledPreferenceKey]; +} + +- (NSURL *)userStyleSheetLocation +{ + NSString *locationString = [self _stringValueForKey: WebKitUserStyleSheetLocationPreferenceKey]; + + if ([locationString _webkit_looksLikeAbsoluteURL]) { + return [NSURL _web_URLWithDataAsString:locationString]; + } else { + locationString = [locationString stringByExpandingTildeInPath]; + return [NSURL fileURLWithPath:locationString]; + } +} + +- (void)setUserStyleSheetLocation:(NSURL *)URL +{ + NSString *locationString; + + if ([URL isFileURL]) { + locationString = [[URL path] _web_stringByAbbreviatingWithTildeInPath]; + } else { + locationString = [URL _web_originalDataAsString]; + } + + if (!locationString) + locationString = @""; + + [self _setStringValue:locationString forKey: WebKitUserStyleSheetLocationPreferenceKey]; +} + +- (BOOL)shouldPrintBackgrounds +{ + return [self _boolValueForKey: WebKitShouldPrintBackgroundsPreferenceKey]; +} + +- (void)setShouldPrintBackgrounds:(BOOL)flag +{ + [self _setBoolValue: flag forKey: WebKitShouldPrintBackgroundsPreferenceKey]; +} + +- (BOOL)isJavaEnabled +{ + return [self _boolValueForKey: WebKitJavaEnabledPreferenceKey]; +} + +- (void)setJavaEnabled:(BOOL)flag +{ + [self _setBoolValue: flag forKey: WebKitJavaEnabledPreferenceKey]; +} + +- (BOOL)isJavaScriptEnabled +{ + return [self _boolValueForKey: WebKitJavaScriptEnabledPreferenceKey]; +} + +- (void)setJavaScriptEnabled:(BOOL)flag +{ + [self _setBoolValue: flag forKey: WebKitJavaScriptEnabledPreferenceKey]; +} + +- (BOOL)javaScriptCanOpenWindowsAutomatically +{ + return [self _boolValueForKey: WebKitJavaScriptCanOpenWindowsAutomaticallyPreferenceKey]; +} + +- (void)setJavaScriptCanOpenWindowsAutomatically:(BOOL)flag +{ + [self _setBoolValue: flag forKey: WebKitJavaScriptCanOpenWindowsAutomaticallyPreferenceKey]; +} + +- (BOOL)arePlugInsEnabled +{ + return [self _boolValueForKey: WebKitPluginsEnabledPreferenceKey]; +} + +- (void)setPlugInsEnabled:(BOOL)flag +{ + [self _setBoolValue: flag forKey: WebKitPluginsEnabledPreferenceKey]; +} + +- (BOOL)allowsAnimatedImages +{ + return [self _boolValueForKey: WebKitAllowAnimatedImagesPreferenceKey]; +} + +- (void)setAllowsAnimatedImages:(BOOL)flag +{ + [self _setBoolValue: flag forKey: WebKitAllowAnimatedImagesPreferenceKey]; +} + +- (BOOL)allowsAnimatedImageLooping +{ + return [self _boolValueForKey: WebKitAllowAnimatedImageLoopingPreferenceKey]; +} + +- (void)setAllowsAnimatedImageLooping: (BOOL)flag +{ + [self _setBoolValue: flag forKey: WebKitAllowAnimatedImageLoopingPreferenceKey]; +} + +- (void)setLoadsImagesAutomatically: (BOOL)flag +{ + [self _setBoolValue: flag forKey: WebKitDisplayImagesKey]; +} + +- (BOOL)loadsImagesAutomatically +{ + return [self _boolValueForKey: WebKitDisplayImagesKey]; +} + +- (void)setAutosaves:(BOOL)flag +{ + _private->autosaves = flag; +} + +- (BOOL)autosaves +{ + return _private->autosaves; +} + +- (void)setTabsToLinks:(BOOL)flag +{ + [self _setBoolValue: flag forKey: WebKitTabToLinksPreferenceKey]; +} + +- (BOOL)tabsToLinks +{ + return [self _boolValueForKey:WebKitTabToLinksPreferenceKey]; +} + +- (void)setPrivateBrowsingEnabled:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitPrivateBrowsingEnabledPreferenceKey]; +} + +- (BOOL)privateBrowsingEnabled +{ + return [self _boolValueForKey:WebKitPrivateBrowsingEnabledPreferenceKey]; +} + +- (void)setUsesPageCache:(BOOL)usesPageCache +{ + [self _setBoolValue:usesPageCache forKey:WebKitUsesPageCachePreferenceKey]; +} + +- (BOOL)usesPageCache +{ + return [self _boolValueForKey:WebKitUsesPageCachePreferenceKey]; +} + +- (void)setCacheModel:(WebCacheModel)cacheModel +{ + [self _setIntegerValue:cacheModel forKey:WebKitCacheModelPreferenceKey]; + [self setAutomaticallyDetectsCacheModel:NO]; +} + +- (WebCacheModel)cacheModel +{ + return [self _integerValueForKey:WebKitCacheModelPreferenceKey]; +} + +@end + +@implementation WebPreferences (WebPrivate) + +- (BOOL)isDNSPrefetchingEnabled +{ + return [self _boolValueForKey:WebKitDNSPrefetchingEnabledPreferenceKey]; +} + +- (void)setDNSPrefetchingEnabled:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitDNSPrefetchingEnabledPreferenceKey]; +} + +- (BOOL)developerExtrasEnabled +{ + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + if ([defaults boolForKey:@"DisableWebKitDeveloperExtras"]) + return NO; +#ifdef NDEBUG + if ([defaults boolForKey:@"WebKitDeveloperExtras"] || [defaults boolForKey:@"IncludeDebugMenu"]) + return YES; + return [self _boolValueForKey:WebKitDeveloperExtrasEnabledPreferenceKey]; +#else + return YES; // always enable in debug builds +#endif +} + +- (void)setDeveloperExtrasEnabled:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitDeveloperExtrasEnabledPreferenceKey]; +} + +- (BOOL)authorAndUserStylesEnabled +{ + return [self _boolValueForKey:WebKitAuthorAndUserStylesEnabledPreferenceKey]; +} + +- (void)setAuthorAndUserStylesEnabled:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitAuthorAndUserStylesEnabledPreferenceKey]; +} + +- (BOOL)applicationChromeModeEnabled +{ + return [self _boolValueForKey:WebKitApplicationChromeModeEnabledPreferenceKey]; +} + +- (void)setApplicationChromeModeEnabled:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitApplicationChromeModeEnabledPreferenceKey]; +} + +- (BOOL)webArchiveDebugModeEnabled +{ + return [self _boolValueForKey:WebKitWebArchiveDebugModeEnabledPreferenceKey]; +} + +- (void)setWebArchiveDebugModeEnabled:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitWebArchiveDebugModeEnabledPreferenceKey]; +} + +- (BOOL)localFileContentSniffingEnabled +{ + return [self _boolValueForKey:WebKitLocalFileContentSniffingEnabledPreferenceKey]; +} + +- (void)setLocalFileContentSniffingEnabled:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitLocalFileContentSniffingEnabledPreferenceKey]; +} + +- (BOOL)offlineWebApplicationCacheEnabled +{ + return [self _boolValueForKey:WebKitOfflineWebApplicationCacheEnabledPreferenceKey]; +} + +- (void)setOfflineWebApplicationCacheEnabled:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitOfflineWebApplicationCacheEnabledPreferenceKey]; +} + +- (BOOL)zoomsTextOnly +{ + return [self _boolValueForKey:WebKitZoomsTextOnlyPreferenceKey]; +} + +- (void)setZoomsTextOnly:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitZoomsTextOnlyPreferenceKey]; +} + +- (BOOL)javaScriptCanAccessClipboard +{ + return [self _boolValueForKey:WebKitJavaScriptCanAccessClipboardPreferenceKey]; +} + +- (void)setJavaScriptCanAccessClipboard:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitJavaScriptCanAccessClipboardPreferenceKey]; +} + +- (BOOL)isXSSAuditorEnabled +{ + return [self _boolValueForKey:WebKitXSSAuditorEnabledPreferenceKey]; +} + +- (void)setXSSAuditorEnabled:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitXSSAuditorEnabledPreferenceKey]; +} + +- (BOOL)respectStandardStyleKeyEquivalents +{ + return [self _boolValueForKey:WebKitRespectStandardStyleKeyEquivalentsPreferenceKey]; +} + +- (void)setRespectStandardStyleKeyEquivalents:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitRespectStandardStyleKeyEquivalentsPreferenceKey]; +} + +- (BOOL)showsURLsInToolTips +{ + return [self _boolValueForKey:WebKitShowsURLsInToolTipsPreferenceKey]; +} + +- (void)setShowsURLsInToolTips:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitShowsURLsInToolTipsPreferenceKey]; +} + +- (BOOL)textAreasAreResizable +{ + return [self _boolValueForKey: WebKitTextAreasAreResizablePreferenceKey]; +} + +- (void)setTextAreasAreResizable:(BOOL)flag +{ + [self _setBoolValue: flag forKey: WebKitTextAreasAreResizablePreferenceKey]; +} + +- (BOOL)shrinksStandaloneImagesToFit +{ + return [self _boolValueForKey:WebKitShrinksStandaloneImagesToFitPreferenceKey]; +} + +- (void)setShrinksStandaloneImagesToFit:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitShrinksStandaloneImagesToFitPreferenceKey]; +} + +- (BOOL)automaticallyDetectsCacheModel +{ + return _private->automaticallyDetectsCacheModel; +} + +- (void)setAutomaticallyDetectsCacheModel:(BOOL)automaticallyDetectsCacheModel +{ + _private->automaticallyDetectsCacheModel = automaticallyDetectsCacheModel; +} + +- (BOOL)usesEncodingDetector +{ + return [self _boolValueForKey: WebKitUsesEncodingDetectorPreferenceKey]; +} + +- (void)setUsesEncodingDetector:(BOOL)flag +{ + [self _setBoolValue: flag forKey: WebKitUsesEncodingDetectorPreferenceKey]; +} + +- (BOOL)isWebSecurityEnabled +{ + return [self _boolValueForKey: WebKitWebSecurityEnabledPreferenceKey]; +} + +- (void)setWebSecurityEnabled:(BOOL)flag +{ + [self _setBoolValue: flag forKey: WebKitWebSecurityEnabledPreferenceKey]; +} + +- (BOOL)allowUniversalAccessFromFileURLs +{ + return [self _boolValueForKey: WebKitAllowUniversalAccessFromFileURLsPreferenceKey]; +} + +- (void)setAllowUniversalAccessFromFileURLs:(BOOL)flag +{ + [self _setBoolValue: flag forKey: WebKitAllowUniversalAccessFromFileURLsPreferenceKey]; +} + +- (BOOL)allowFileAccessFromFileURLs +{ + return [self _boolValueForKey: WebKitAllowFileAccessFromFileURLsPreferenceKey]; +} + +- (void)setAllowFileAccessFromFileURLs:(BOOL)flag +{ + [self _setBoolValue: flag forKey: WebKitAllowFileAccessFromFileURLsPreferenceKey]; +} + +- (NSTimeInterval)_backForwardCacheExpirationInterval +{ + // FIXME: There's probably no good reason to read from the standard user defaults instead of self. + return (NSTimeInterval)[[NSUserDefaults standardUserDefaults] floatForKey:WebKitBackForwardCacheExpirationIntervalKey]; +} + +- (float)PDFScaleFactor +{ + return [self _floatValueForKey:WebKitPDFScaleFactorPreferenceKey]; +} + +- (void)setPDFScaleFactor:(float)factor +{ + [self _setFloatValue:factor forKey:WebKitPDFScaleFactorPreferenceKey]; +} + +- (int64_t)applicationCacheTotalQuota +{ + return [self _longLongValueForKey:WebKitApplicationCacheTotalQuota]; +} + +- (void)setApplicationCacheTotalQuota:(int64_t)quota +{ + [self _setLongLongValue:quota forKey:WebKitApplicationCacheTotalQuota]; + + // Application Cache Preferences are stored on the global cache storage manager, not in Settings. + [WebApplicationCache setMaximumSize:quota]; +} + +- (int64_t)applicationCacheDefaultOriginQuota +{ + return [self _longLongValueForKey:WebKitApplicationCacheDefaultOriginQuota]; +} + +- (void)setApplicationCacheDefaultOriginQuota:(int64_t)quota +{ + [self _setLongLongValue:quota forKey:WebKitApplicationCacheDefaultOriginQuota]; +} + +- (PDFDisplayMode)PDFDisplayMode +{ + PDFDisplayMode value = [self _integerValueForKey:WebKitPDFDisplayModePreferenceKey]; + if (value != kPDFDisplaySinglePage && value != kPDFDisplaySinglePageContinuous && value != kPDFDisplayTwoUp && value != kPDFDisplayTwoUpContinuous) { + // protect against new modes from future versions of OS X stored in defaults + value = kPDFDisplaySinglePageContinuous; + } + return value; +} + +- (void)setPDFDisplayMode:(PDFDisplayMode)mode +{ + [self _setIntegerValue:mode forKey:WebKitPDFDisplayModePreferenceKey]; +} + +- (WebKitEditableLinkBehavior)editableLinkBehavior +{ + WebKitEditableLinkBehavior value = static_cast<WebKitEditableLinkBehavior> ([self _integerValueForKey:WebKitEditableLinkBehaviorPreferenceKey]); + if (value != WebKitEditableLinkDefaultBehavior && + value != WebKitEditableLinkAlwaysLive && + value != WebKitEditableLinkNeverLive && + value != WebKitEditableLinkOnlyLiveWithShiftKey && + value != WebKitEditableLinkLiveWhenNotFocused) { + // ensure that a valid result is returned + value = WebKitEditableLinkDefaultBehavior; + } + + return value; +} + +- (void)setEditableLinkBehavior:(WebKitEditableLinkBehavior)behavior +{ + [self _setIntegerValue:behavior forKey:WebKitEditableLinkBehaviorPreferenceKey]; +} + +- (WebTextDirectionSubmenuInclusionBehavior)textDirectionSubmenuInclusionBehavior +{ + WebTextDirectionSubmenuInclusionBehavior value = static_cast<WebTextDirectionSubmenuInclusionBehavior>([self _integerValueForKey:WebKitTextDirectionSubmenuInclusionBehaviorPreferenceKey]); + if (value != WebTextDirectionSubmenuNeverIncluded && + value != WebTextDirectionSubmenuAutomaticallyIncluded && + value != WebTextDirectionSubmenuAlwaysIncluded) { + // Ensure that a valid result is returned. + value = WebTextDirectionSubmenuNeverIncluded; + } + return value; +} + +- (void)setTextDirectionSubmenuInclusionBehavior:(WebTextDirectionSubmenuInclusionBehavior)behavior +{ + [self _setIntegerValue:behavior forKey:WebKitTextDirectionSubmenuInclusionBehaviorPreferenceKey]; +} + +- (BOOL)_useSiteSpecificSpoofing +{ + return [self _boolValueForKey:WebKitUseSiteSpecificSpoofingPreferenceKey]; +} + +- (void)_setUseSiteSpecificSpoofing:(BOOL)newValue +{ + [self _setBoolValue:newValue forKey:WebKitUseSiteSpecificSpoofingPreferenceKey]; +} + +- (BOOL)databasesEnabled +{ + return [self _boolValueForKey:WebKitDatabasesEnabledPreferenceKey]; +} + +- (void)setDatabasesEnabled:(BOOL)databasesEnabled +{ + [self _setBoolValue:databasesEnabled forKey:WebKitDatabasesEnabledPreferenceKey]; +} + +- (BOOL)localStorageEnabled +{ + return [self _boolValueForKey:WebKitLocalStorageEnabledPreferenceKey]; +} + +- (void)setLocalStorageEnabled:(BOOL)localStorageEnabled +{ + [self _setBoolValue:localStorageEnabled forKey:WebKitLocalStorageEnabledPreferenceKey]; +} + +- (BOOL)experimentalNotificationsEnabled +{ + return [self _boolValueForKey:WebKitExperimentalNotificationsEnabledPreferenceKey]; +} + +- (void)setExperimentalNotificationsEnabled:(BOOL)experimentalNotificationsEnabled +{ + [self _setBoolValue:experimentalNotificationsEnabled forKey:WebKitExperimentalNotificationsEnabledPreferenceKey]; +} + ++ (WebPreferences *)_getInstanceForIdentifier:(NSString *)ident +{ + LOG(Encoding, "requesting for %@\n", ident); + + if (!ident) + return _standardPreferences; + + WebPreferences *instance = [webPreferencesInstances objectForKey:[self _concatenateKeyWithIBCreatorID:ident]]; + + return instance; +} + ++ (void)_setInstance:(WebPreferences *)instance forIdentifier:(NSString *)ident +{ + if (!webPreferencesInstances) + webPreferencesInstances = [[NSMutableDictionary alloc] init]; + if (ident) { + [webPreferencesInstances setObject:instance forKey:[self _concatenateKeyWithIBCreatorID:ident]]; + LOG(Encoding, "recording %p for %@\n", instance, [self _concatenateKeyWithIBCreatorID:ident]); + } +} + ++ (void)_checkLastReferenceForIdentifier:(id)identifier +{ + // FIXME: This won't work at all under garbage collection because retainCount returns a constant. + // We may need to change WebPreferences API so there's an explicit way to end the lifetime of one. + WebPreferences *instance = [webPreferencesInstances objectForKey:identifier]; + if ([instance retainCount] == 1) + [webPreferencesInstances removeObjectForKey:identifier]; +} + ++ (void)_removeReferenceForIdentifier:(NSString *)ident +{ + if (ident) + [self performSelector:@selector(_checkLastReferenceForIdentifier:) withObject:[self _concatenateKeyWithIBCreatorID:ident] afterDelay:0.1]; +} + +- (void)_postPreferencesChangesNotification +{ + if (!pthread_main_np()) { + [self performSelectorOnMainThread:_cmd withObject:nil waitUntilDone:NO]; + return; + } + + [[NSNotificationCenter defaultCenter] + postNotificationName:WebPreferencesChangedNotification object:self + userInfo:nil]; +} + ++ (CFStringEncoding)_systemCFStringEncoding +{ + return WKGetWebDefaultCFStringEncoding(); +} + ++ (void)_setInitialDefaultTextEncodingToSystemEncoding +{ + NSString *systemEncodingName = (NSString *)CFStringConvertEncodingToIANACharSetName([self _systemCFStringEncoding]); + + // CFStringConvertEncodingToIANACharSetName() returns cp949 for kTextEncodingDOSKorean AKA "extended EUC-KR" AKA windows-949. + // ICU uses this name for a different encoding, so we need to change the name to a value that actually gives us windows-949. + // In addition, this value must match what is used in Safari, see <rdar://problem/5579292>. + // On some OS versions, the result is CP949 (uppercase). + if ([systemEncodingName _webkit_isCaseInsensitiveEqualToString:@"cp949"]) + systemEncodingName = @"ks_c_5601-1987"; + [[NSUserDefaults standardUserDefaults] registerDefaults: + [NSDictionary dictionaryWithObject:systemEncodingName forKey:WebKitDefaultTextEncodingNamePreferenceKey]]; +} + +static NSString *classIBCreatorID = nil; + ++ (void)_setIBCreatorID:(NSString *)string +{ + NSString *old = classIBCreatorID; + classIBCreatorID = [string copy]; + [old release]; +} + +- (BOOL)isDOMPasteAllowed +{ + return [self _boolValueForKey:WebKitDOMPasteAllowedPreferenceKey]; +} + +- (void)setDOMPasteAllowed:(BOOL)DOMPasteAllowed +{ + [self _setBoolValue:DOMPasteAllowed forKey:WebKitDOMPasteAllowedPreferenceKey]; +} + +- (NSString *)_localStorageDatabasePath +{ + return [[self _stringValueForKey:WebKitLocalStorageDatabasePathPreferenceKey] stringByStandardizingPath]; +} + +- (void)_setLocalStorageDatabasePath:(NSString *)path +{ + [self _setStringValue:[path stringByStandardizingPath] forKey:WebKitLocalStorageDatabasePathPreferenceKey]; +} + +- (NSString *)_ftpDirectoryTemplatePath +{ + return [[self _stringValueForKey:WebKitFTPDirectoryTemplatePath] stringByStandardizingPath]; +} + +- (void)_setFTPDirectoryTemplatePath:(NSString *)path +{ + [self _setStringValue:[path stringByStandardizingPath] forKey:WebKitFTPDirectoryTemplatePath]; +} + +- (BOOL)_forceFTPDirectoryListings +{ + return [self _boolValueForKey:WebKitForceFTPDirectoryListings]; +} + +- (void)_setForceFTPDirectoryListings:(BOOL)force +{ + [self _setBoolValue:force forKey:WebKitForceFTPDirectoryListings]; +} + +- (BOOL)acceleratedCompositingEnabled +{ + return [self _boolValueForKey:WebKitAcceleratedCompositingEnabledPreferenceKey]; +} + +- (void)setAcceleratedCompositingEnabled:(BOOL)enabled +{ + [self _setBoolValue:enabled forKey:WebKitAcceleratedCompositingEnabledPreferenceKey]; +} + +- (BOOL)showDebugBorders +{ + return [self _boolValueForKey:WebKitShowDebugBordersPreferenceKey]; +} + +- (void)setShowDebugBorders:(BOOL)enabled +{ + [self _setBoolValue:enabled forKey:WebKitShowDebugBordersPreferenceKey]; +} + +- (BOOL)showRepaintCounter +{ + return [self _boolValueForKey:WebKitShowRepaintCounterPreferenceKey]; +} + +- (void)setShowRepaintCounter:(BOOL)enabled +{ + [self _setBoolValue:enabled forKey:WebKitShowRepaintCounterPreferenceKey]; +} + +- (BOOL)webGLEnabled +{ + return [self _boolValueForKey:WebKitWebGLEnabledPreferenceKey]; +} + +- (void)setWebGLEnabled:(BOOL)enabled +{ + [self _setBoolValue:enabled forKey:WebKitWebGLEnabledPreferenceKey]; +} + +- (BOOL)accelerated2dCanvasEnabled +{ + return [self _boolValueForKey:WebKitAccelerated2dCanvasEnabledPreferenceKey]; +} + +- (void)setAccelerated2dCanvasEnabled:(BOOL)enabled +{ + [self _setBoolValue:enabled forKey:WebKitAccelerated2dCanvasEnabledPreferenceKey]; +} + +- (unsigned)pluginAllowedRunTime +{ + return [self _integerValueForKey:WebKitPluginAllowedRunTimePreferenceKey]; +} + +- (void)setPluginAllowedRunTime:(unsigned)allowedRunTime +{ + return [self _setIntegerValue:allowedRunTime forKey:WebKitPluginAllowedRunTimePreferenceKey]; +} + +- (BOOL)isFrameFlatteningEnabled +{ + return [self _boolValueForKey:WebKitFrameFlatteningEnabledPreferenceKey]; +} + +- (void)setFrameFlatteningEnabled:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitFrameFlatteningEnabledPreferenceKey]; +} + +- (BOOL)isSpatialNavigationEnabled +{ + return [self _boolValueForKey:WebKitSpatialNavigationEnabledPreferenceKey]; +} + +- (void)setSpatialNavigationEnabled:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitSpatialNavigationEnabledPreferenceKey]; +} + +- (BOOL)paginateDuringLayoutEnabled +{ + return [self _boolValueForKey:WebKitPaginateDuringLayoutEnabledPreferenceKey]; +} + +- (void)setPaginateDuringLayoutEnabled:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitPaginateDuringLayoutEnabledPreferenceKey]; +} + +- (BOOL)memoryInfoEnabled +{ + return [self _boolValueForKey:WebKitMemoryInfoEnabledPreferenceKey]; +} + +- (void)setMemoryInfoEnabled:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitMemoryInfoEnabledPreferenceKey]; +} + +- (BOOL)hyperlinkAuditingEnabled +{ + return [self _boolValueForKey:WebKitHyperlinkAuditingEnabledPreferenceKey]; +} + +- (void)setHyperlinkAuditingEnabled:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitHyperlinkAuditingEnabledPreferenceKey]; +} + +- (WebKitEditingBehavior)editingBehavior +{ + return static_cast<WebKitEditingBehavior>([self _integerValueForKey:WebKitEditingBehaviorPreferenceKey]); +} + +- (void)setEditingBehavior:(WebKitEditingBehavior)behavior +{ + [self _setIntegerValue:behavior forKey:WebKitEditingBehaviorPreferenceKey]; +} + +- (BOOL)usePreHTML5ParserQuirks +{ + return [self _boolValueForKey:WebKitUsePreHTML5ParserQuirksKey]; +} + +- (void)setUsePreHTML5ParserQuirks:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitUsePreHTML5ParserQuirksKey]; +} + +- (void)didRemoveFromWebView +{ + ASSERT(_private->numWebViews); + if (--_private->numWebViews == 0) + [[NSNotificationCenter defaultCenter] + postNotificationName:WebPreferencesRemovedNotification + object:self + userInfo:nil]; +} + +- (void)willAddToWebView +{ + ++_private->numWebViews; +} + +- (void)_setPreferenceForTestWithValue:(NSString *)value forKey:(NSString *)key +{ + [self _setStringValue:value forKey:key]; +} + +- (void)setFullScreenEnabled:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitFullScreenEnabledPreferenceKey]; +} + +- (BOOL)fullScreenEnabled +{ + return [self _boolValueForKey:WebKitFullScreenEnabledPreferenceKey]; +} + +- (void)setAsynchronousSpellCheckingEnabled:(BOOL)flag +{ + [self _setBoolValue:flag forKey:WebKitAsynchronousSpellCheckingEnabledPreferenceKey]; +} + +- (BOOL)asynchronousSpellCheckingEnabled +{ + return [self _boolValueForKey:WebKitAsynchronousSpellCheckingEnabledPreferenceKey]; +} + ++ (void)setWebKitLinkTimeVersion:(int)version +{ + setWebKitLinkTimeVersion(version); +} + +@end + +@implementation WebPreferences (WebInternal) + ++ (NSString *)_IBCreatorID +{ + return classIBCreatorID; +} + ++ (NSString *)_concatenateKeyWithIBCreatorID:(NSString *)key +{ + NSString *IBCreatorID = [WebPreferences _IBCreatorID]; + if (!IBCreatorID) + return key; + return [IBCreatorID stringByAppendingString:key]; +} + +@end diff --git a/Source/WebKit/mac/WebView/WebPreferencesPrivate.h b/Source/WebKit/mac/WebView/WebPreferencesPrivate.h new file mode 100644 index 0000000..89b6288 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebPreferencesPrivate.h @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2005, 2007 Apple Computer, Inc. All rights reserved. + * + * 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 <WebKit/WebPreferences.h> +#import <Quartz/Quartz.h> + +typedef enum { + WebKitEditableLinkDefaultBehavior, + WebKitEditableLinkAlwaysLive, + WebKitEditableLinkOnlyLiveWithShiftKey, + WebKitEditableLinkLiveWhenNotFocused, + WebKitEditableLinkNeverLive +} WebKitEditableLinkBehavior; + +typedef enum { + WebTextDirectionSubmenuNeverIncluded, + WebTextDirectionSubmenuAutomaticallyIncluded, + WebTextDirectionSubmenuAlwaysIncluded +} WebTextDirectionSubmenuInclusionBehavior; + +typedef enum { + WebKitEditingMacBehavior, + WebKitEditingWinBehavior, + WebKitEditingUnixBehavior +} WebKitEditingBehavior; + +extern NSString *WebPreferencesChangedNotification; +extern NSString *WebPreferencesRemovedNotification; + +@interface WebPreferences (WebPrivate) + +// Preferences that might be public in a future release + +- (BOOL)isDNSPrefetchingEnabled; +- (void)setDNSPrefetchingEnabled:(BOOL)flag; + +- (BOOL)developerExtrasEnabled; +- (void)setDeveloperExtrasEnabled:(BOOL)flag; + +- (BOOL)authorAndUserStylesEnabled; +- (void)setAuthorAndUserStylesEnabled:(BOOL)flag; + +- (BOOL)applicationChromeModeEnabled; +- (void)setApplicationChromeModeEnabled:(BOOL)flag; + +- (BOOL)usesEncodingDetector; +- (void)setUsesEncodingDetector:(BOOL)flag; + +- (BOOL)respectStandardStyleKeyEquivalents; +- (void)setRespectStandardStyleKeyEquivalents:(BOOL)flag; + +- (BOOL)showsURLsInToolTips; +- (void)setShowsURLsInToolTips:(BOOL)flag; + +- (BOOL)textAreasAreResizable; +- (void)setTextAreasAreResizable:(BOOL)flag; + +- (PDFDisplayMode)PDFDisplayMode; +- (void)setPDFDisplayMode:(PDFDisplayMode)mode; + +- (BOOL)shrinksStandaloneImagesToFit; +- (void)setShrinksStandaloneImagesToFit:(BOOL)flag; + +- (BOOL)automaticallyDetectsCacheModel; +- (void)setAutomaticallyDetectsCacheModel:(BOOL)automaticallyDetectsCacheModel; + +- (BOOL)webArchiveDebugModeEnabled; +- (void)setWebArchiveDebugModeEnabled:(BOOL)webArchiveDebugModeEnabled; + +- (BOOL)localFileContentSniffingEnabled; +- (void)setLocalFileContentSniffingEnabled:(BOOL)localFileContentSniffingEnabled; + +- (BOOL)offlineWebApplicationCacheEnabled; +- (void)setOfflineWebApplicationCacheEnabled:(BOOL)offlineWebApplicationCacheEnabled; + +- (BOOL)databasesEnabled; +- (void)setDatabasesEnabled:(BOOL)databasesEnabled; + +- (BOOL)localStorageEnabled; +- (void)setLocalStorageEnabled:(BOOL)localStorageEnabled; + +- (BOOL)isWebSecurityEnabled; +- (void)setWebSecurityEnabled:(BOOL)flag; + +- (BOOL)allowUniversalAccessFromFileURLs; +- (void)setAllowUniversalAccessFromFileURLs:(BOOL)flag; + +- (BOOL)allowFileAccessFromFileURLs; +- (void)setAllowFileAccessFromFileURLs:(BOOL)flag; + +- (BOOL)zoomsTextOnly; +- (void)setZoomsTextOnly:(BOOL)zoomsTextOnly; + +- (BOOL)javaScriptCanAccessClipboard; +- (void)setJavaScriptCanAccessClipboard:(BOOL)flag; + +- (BOOL)isXSSAuditorEnabled; +- (void)setXSSAuditorEnabled:(BOOL)flag; + +- (BOOL)experimentalNotificationsEnabled; +- (void)setExperimentalNotificationsEnabled:(BOOL)notificationsEnabled; + +- (unsigned)pluginAllowedRunTime; +- (void)setPluginAllowedRunTime:(unsigned)allowedRunTime; + +- (BOOL)isFrameFlatteningEnabled; +- (void)setFrameFlatteningEnabled:(BOOL)flag; + +- (BOOL)isSpatialNavigationEnabled; +- (void)setSpatialNavigationEnabled:(BOOL)flag; + +// zero means do AutoScale +- (float)PDFScaleFactor; +- (void)setPDFScaleFactor:(float)scale; + +- (int64_t)applicationCacheTotalQuota; +- (void)setApplicationCacheTotalQuota:(int64_t)quota; + +- (int64_t)applicationCacheDefaultOriginQuota; +- (void)setApplicationCacheDefaultOriginQuota:(int64_t)quota; + +- (WebKitEditableLinkBehavior)editableLinkBehavior; +- (void)setEditableLinkBehavior:(WebKitEditableLinkBehavior)behavior; + +- (WebKitEditingBehavior)editingBehavior; +- (void)setEditingBehavior:(WebKitEditingBehavior)behavior; + +- (WebTextDirectionSubmenuInclusionBehavior)textDirectionSubmenuInclusionBehavior; +- (void)setTextDirectionSubmenuInclusionBehavior:(WebTextDirectionSubmenuInclusionBehavior)behavior; + +// Used to set preference specified in the test via LayoutTestController.overridePreference(..). +// For use with DumpRenderTree only. +- (void)_setPreferenceForTestWithValue:(NSString *)value forKey:(NSString *)key; + +// If site-specific spoofing is enabled, some pages that do inappropriate user-agent string checks will be +// passed a nonstandard user-agent string to get them to work correctly. This method might be removed in +// the future when there's no more need for it. +- (BOOL)_useSiteSpecificSpoofing; +- (void)_setUseSiteSpecificSpoofing:(BOOL)newValue; + +// WARNING: Allowing paste through the DOM API opens a security hole. We only use it for testing purposes. +- (BOOL)isDOMPasteAllowed; +- (void)setDOMPasteAllowed:(BOOL)DOMPasteAllowed; + +- (NSString *)_ftpDirectoryTemplatePath; +- (void)_setFTPDirectoryTemplatePath:(NSString *)path; + +- (void)_setForceFTPDirectoryListings:(BOOL)force; +- (BOOL)_forceFTPDirectoryListings; + +- (NSString *)_localStorageDatabasePath; +- (void)_setLocalStorageDatabasePath:(NSString *)path; + +- (BOOL)acceleratedCompositingEnabled; +- (void)setAcceleratedCompositingEnabled:(BOOL)enabled; + +- (BOOL)showDebugBorders; +- (void)setShowDebugBorders:(BOOL)show; + +- (BOOL)showRepaintCounter; +- (void)setShowRepaintCounter:(BOOL)show; + +- (BOOL)webGLEnabled; +- (void)setWebGLEnabled:(BOOL)enabled; + +- (BOOL)accelerated2dCanvasEnabled; +- (void)setAccelerated2dCanvasEnabled:(BOOL)enabled; + +- (BOOL)paginateDuringLayoutEnabled; +- (void)setPaginateDuringLayoutEnabled:(BOOL)flag; + +- (BOOL)memoryInfoEnabled; +- (void)setMemoryInfoEnabled:(BOOL)enabled; + +- (BOOL)hyperlinkAuditingEnabled; +- (void)setHyperlinkAuditingEnabled:(BOOL)enabled; + +// Other private methods +- (void)_postPreferencesChangesNotification; ++ (WebPreferences *)_getInstanceForIdentifier:(NSString *)identifier; ++ (void)_setInstance:(WebPreferences *)instance forIdentifier:(NSString *)identifier; ++ (void)_removeReferenceForIdentifier:(NSString *)identifier; +- (NSTimeInterval)_backForwardCacheExpirationInterval; ++ (CFStringEncoding)_systemCFStringEncoding; ++ (void)_setInitialDefaultTextEncodingToSystemEncoding; ++ (void)_setIBCreatorID:(NSString *)string; + ++ (void)setWebKitLinkTimeVersion:(int)version; + +// For WebView's use only. +- (void)willAddToWebView; +- (void)didRemoveFromWebView; + +// Full screen support is dependent on WebCore/WebKit being +// compiled with ENABLE_FULLSCREEN_API. +- (void)setFullScreenEnabled:(BOOL)flag; +- (BOOL)fullScreenEnabled; + +- (void)setAsynchronousSpellCheckingEnabled:(BOOL)flag; +- (BOOL)asynchronousSpellCheckingEnabled; + +- (void)setUsePreHTML5ParserQuirks:(BOOL)flag; +- (BOOL)usePreHTML5ParserQuirks; + +@end diff --git a/Source/WebKit/mac/WebView/WebRenderLayer.h b/Source/WebKit/mac/WebView/WebRenderLayer.h new file mode 100644 index 0000000..e09983d --- /dev/null +++ b/Source/WebKit/mac/WebView/WebRenderLayer.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 COMPUTER, INC. OR + * 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 <Foundation/Foundation.h> + +@class WebFrame; + +@interface WebRenderLayer : NSObject +{ + NSArray *children; + NSString *name; + NSString *compositingInfo; + NSRect bounds; + BOOL composited; + BOOL separator; +} + +- (id)initWithWebFrame:(WebFrame *)frame; + +- (NSArray *)children; + +- (NSString *)name; +- (NSString *)positionString; +- (NSString *)widthString; +- (NSString *)heightString; +- (NSString *)compositingInfo; + +- (BOOL)isComposited; +- (BOOL)isSeparator; + +@end diff --git a/Source/WebKit/mac/WebView/WebRenderLayer.mm b/Source/WebKit/mac/WebView/WebRenderLayer.mm new file mode 100644 index 0000000..c282643 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebRenderLayer.mm @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 COMPUTER, INC. OR + * 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 "WebRenderLayer.h" + +#import "WebFrameInternal.h" +#import <WebCore/Frame.h> +#import <WebCore/FrameLoaderClient.h> +#import <WebCore/PlatformString.h> +#import <WebCore/RenderLayer.h> +#import <WebCore/RenderLayerBacking.h> +#import <WebCore/RenderView.h> +#import <WebCore/StyledElement.h> + +using namespace WebCore; + + +@interface WebRenderLayer(Private) + +- (id)initWithRenderLayer:(RenderLayer *)layer; +- (void)buildDescendantLayers:(RenderLayer*)rootLayer; + +@end + +@implementation WebRenderLayer + ++ (NSString *)nameForLayer:(RenderLayer*)layer +{ + RenderObject* renderer = layer->renderer(); + NSString *name = [NSString stringWithUTF8String:renderer->renderName()]; + + if (Node* node = renderer->node()) { + if (node->isElementNode()) + name = [name stringByAppendingFormat:@" %@", (NSString *)static_cast<Element*>(node)->tagName()]; + if (node->hasID()) + name = [name stringByAppendingFormat:@" id=\"%@\"", (NSString *)static_cast<Element*>(node)->getIdAttribute()]; + + if (node->hasClass()) { + StyledElement* styledElement = static_cast<StyledElement*>(node); + String classes; + for (size_t i = 0; i < styledElement->classNames().size(); ++i) { + if (i > 0) + classes += " "; + classes += styledElement->classNames()[i]; + } + name = [name stringByAppendingFormat:@" class=\"%@\"", (NSString *)classes]; + } + } + + if (layer->isReflection()) + name = [name stringByAppendingString:@" (reflection)"]; + + return name; +} + ++ (NSString *)compositingInfoForLayer:(RenderLayer*)layer +{ + if (!layer->isComposited()) + return @""; + + NSString *layerType = @""; +#if USE(ACCELERATED_COMPOSITING) + RenderLayerBacking* backing = layer->backing(); + switch (backing->compositingLayerType()) { + case NormalCompositingLayer: + layerType = @"composited"; + break; + case TiledCompositingLayer: + layerType = @"composited: tiled layer"; + break; + case MediaCompositingLayer: + layerType = @"composited for plug-in, video or WebGL"; + break; + case ContainerCompositingLayer: + layerType = @"composited: container layer"; + break; + } + + if (backing->hasClippingLayer()) + layerType = [layerType stringByAppendingString:@" (clipping)"]; + + if (backing->hasAncestorClippingLayer()) + layerType = [layerType stringByAppendingString:@" (clipped)"]; +#endif + + return layerType; +} + +- (id)initWithRenderLayer:(RenderLayer*)layer +{ + if ((self = [super init])) { + name = [[WebRenderLayer nameForLayer:layer] retain]; + bounds = layer->absoluteBoundingBox(); + composited = layer->isComposited(); + compositingInfo = [[WebRenderLayer compositingInfoForLayer:layer] retain]; + } + + return self; +} + +- (id)initWithName:(NSString*)layerName +{ + if ((self = [super init])) { + name = [layerName copy]; + separator = YES; + } + + return self; +} + +// Only called on the root. +- (id)initWithWebFrame:(WebFrame *)webFrame +{ + self = [super init]; + + Frame* frame = core(webFrame); + if (!frame->loader()->client()->hasHTMLView()) { + [self release]; + return nil; + } + + RenderObject* renderer = frame->contentRenderer(); + if (!renderer) { + [self release]; + return nil; + } + + if (renderer->hasLayer()) { + RenderLayer* layer = toRenderBoxModelObject(renderer)->layer(); + + name = [[WebRenderLayer nameForLayer:layer] retain]; + bounds = layer->absoluteBoundingBox(); + composited = layer->isComposited(); + compositingInfo = [[WebRenderLayer compositingInfoForLayer:layer] retain]; + + [self buildDescendantLayers:layer]; + } + + return self; +} + +- (void)dealloc +{ + [children release]; + [name release]; + [compositingInfo release]; + [super dealloc]; +} + +- (void)buildDescendantLayers:(RenderLayer*)layer +{ + NSMutableArray *childWebLayers = [[NSMutableArray alloc] init]; + + // Build children in back to front order. + + if (Vector<RenderLayer*>* negZOrderList = layer->negZOrderList()) { + size_t listSize = negZOrderList->size(); + + if (listSize) { + WebRenderLayer* newLayer = [[WebRenderLayer alloc] initWithName:@"-ve z-order list"]; + [childWebLayers addObject:newLayer]; + [newLayer release]; + } + + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = negZOrderList->at(i); + + WebRenderLayer* newLayer = [[WebRenderLayer alloc] initWithRenderLayer:curLayer]; + [newLayer buildDescendantLayers:curLayer]; + + [childWebLayers addObject:newLayer]; + [newLayer release]; + } + } + + if (Vector<RenderLayer*>* normalFlowList = layer->normalFlowList()) { + size_t listSize = normalFlowList->size(); + + if (listSize) { + WebRenderLayer* newLayer = [[WebRenderLayer alloc] initWithName:@"normal flow list"]; + [childWebLayers addObject:newLayer]; + [newLayer release]; + } + + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = normalFlowList->at(i); + + WebRenderLayer* newLayer = [[WebRenderLayer alloc] initWithRenderLayer:curLayer]; + [newLayer buildDescendantLayers:curLayer]; + + [childWebLayers addObject:newLayer]; + [newLayer release]; + } + } + + if (Vector<RenderLayer*>* posZOrderList = layer->posZOrderList()) { + size_t listSize = posZOrderList->size(); + + if (listSize) { + WebRenderLayer* newLayer = [[WebRenderLayer alloc] initWithName:@"+ve z-order list"]; + [childWebLayers addObject:newLayer]; + [newLayer release]; + } + + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = posZOrderList->at(i); + + WebRenderLayer* newLayer = [[WebRenderLayer alloc] initWithRenderLayer:curLayer]; + [newLayer buildDescendantLayers:curLayer]; + + [childWebLayers addObject:newLayer]; + [newLayer release]; + } + } + + children = childWebLayers; +} + +- (NSArray *)children +{ + return children; +} + +- (NSString *)name +{ + return name; +} + +- (NSString *)positionString +{ + return [NSString stringWithFormat:@"(%.0f, %.0f)", bounds.origin.x, bounds.origin.y]; +} + +- (NSString *)widthString +{ + return [NSString stringWithFormat:@"%.0f", bounds.size.width]; +} + +- (NSString *)heightString +{ + return [NSString stringWithFormat:@"%.0f", bounds.size.height]; +} + +- (NSString *)compositingInfo +{ + return compositingInfo; +} + +- (BOOL)isComposited +{ + return composited; +} + +- (BOOL)isSeparator +{ + return separator; +} + +@end diff --git a/Source/WebKit/mac/WebView/WebRenderNode.h b/Source/WebKit/mac/WebView/WebRenderNode.h new file mode 100644 index 0000000..901b2cc --- /dev/null +++ b/Source/WebKit/mac/WebView/WebRenderNode.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * 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 <Foundation/Foundation.h> + +@class WebFrame; + +@interface WebRenderNode : NSObject +{ + NSArray *children; + NSString *name; + NSRect rect; + NSPoint absolutePosition; +} + +- (id)initWithWebFrame:(WebFrame *)frame; + +- (NSArray *)children; + +- (NSString *)name; +- (NSString *)positionString; +- (NSString *)absolutePositionString; +- (NSString *)widthString; +- (NSString *)heightString; + +@end diff --git a/Source/WebKit/mac/WebView/WebRenderNode.mm b/Source/WebKit/mac/WebView/WebRenderNode.mm new file mode 100644 index 0000000..eff1929 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebRenderNode.mm @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2005, 2008 Apple Inc. All rights reserved. + * + * 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 "WebRenderNode.h" + +#import "WebFrameInternal.h" +#import <WebCore/Frame.h> +#import <WebCore/FrameLoaderClient.h> +#import <WebCore/RenderText.h> +#import <WebCore/RenderWidget.h> +#import <WebCore/RenderView.h> +#import <WebCore/Widget.h> + +using namespace WebCore; + +static WebRenderNode *copyRenderNode(RenderObject*); + +@implementation WebRenderNode + +- (id)_initWithCoreFrame:(Frame *)frame +{ + [self release]; + + if (!frame->loader()->client()->hasHTMLView()) + return nil; + + RenderObject* renderer = frame->contentRenderer(); + if (!renderer) + return nil; + + return copyRenderNode(renderer); +} + +- (id)_initWithName:(NSString *)n position:(NSPoint)p rect:(NSRect)r coreFrame:(Frame*)coreFrame children:(NSArray *)c +{ + NSMutableArray *collectChildren; + + self = [super init]; + if (!self) + return nil; + + collectChildren = [c mutableCopy]; + + name = [n retain]; + rect = r; + absolutePosition = p; + + if (coreFrame) { + WebRenderNode *node = [[WebRenderNode alloc] _initWithCoreFrame:coreFrame]; + [collectChildren addObject:node]; + [node release]; + } + + children = [collectChildren copy]; + [collectChildren release]; + + return self; +} + +static WebRenderNode *copyRenderNode(RenderObject* node) +{ + NSMutableArray *children = [[NSMutableArray alloc] init]; + for (RenderObject* child = node->firstChild(); child; child = child->nextSibling()) { + WebRenderNode *childCopy = copyRenderNode(child); + [children addObject:childCopy]; + [childCopy release]; + } + + NSString *name = [[NSString alloc] initWithUTF8String:node->renderName()]; + + RenderWidget* renderWidget = node->isWidget() ? toRenderWidget(node) : 0; + Widget* widget = renderWidget ? renderWidget->widget() : 0; + FrameView* frameView = widget && widget->isFrameView() ? static_cast<FrameView*>(widget) : 0; + Frame* frame = frameView ? frameView->frame() : 0; + + // FIXME: broken with transforms + FloatPoint absPos = node->localToAbsolute(FloatPoint()); + int x = 0; + int y = 0; + int width = 0; + int height = 0; + if (node->isBox()) { + RenderBox* box = toRenderBox(node); + x = box->x(); + y = box->y(); + width = box->width(); + height = box->height(); + } else if (node->isText()) { + // FIXME: Preserve old behavior even though it's strange. + RenderText* text = toRenderText(node); + x = text->firstRunX(); + y = text->firstRunY(); + IntRect box = text->linesBoundingBox(); + width = box.width(); + height = box.height(); + } else if (node->isRenderInline()) { + RenderBoxModelObject* inlineFlow = toRenderBoxModelObject(node); + IntRect boundingBox = inlineFlow->borderBoundingBox(); + x = boundingBox.x(); + y = boundingBox.y(); + width = boundingBox.width(); + height = boundingBox.height(); + } + + WebRenderNode *result = [[WebRenderNode alloc] _initWithName:name + position:absPos rect:NSMakeRect(x, y, width, height) + coreFrame:frame children:children]; + + [name release]; + [children release]; + + return result; +} + +- (id)initWithWebFrame:(WebFrame *)frame +{ + return [self _initWithCoreFrame:core(frame)]; +} + +- (void)dealloc +{ + [children release]; + [name release]; + [super dealloc]; +} + +- (NSArray *)children +{ + return children; +} + +- (NSString *)name +{ + return name; +} + +- (NSString *)absolutePositionString +{ + return [NSString stringWithFormat:@"(%.0f, %.0f)", absolutePosition.x, absolutePosition.y]; +} + +- (NSString *)positionString +{ + return [NSString stringWithFormat:@"(%.0f, %.0f)", rect.origin.x, rect.origin.y]; +} + +- (NSString *)widthString +{ + return [NSString stringWithFormat:@"%.0f", rect.size.width]; +} + +- (NSString *)heightString +{ + return [NSString stringWithFormat:@"%.0f", rect.size.height]; +} + +@end diff --git a/Source/WebKit/mac/WebView/WebResource.h b/Source/WebKit/mac/WebView/WebResource.h new file mode 100644 index 0000000..7d817b7 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebResource.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2004, 2005 Apple Computer, Inc. All rights reserved. + * + * 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 <Foundation/Foundation.h> + +@class WebResourcePrivate; + + +/*! + @class WebResource + @discussion A WebResource represents a fully downloaded URL. + It includes the data of the resource as well as the metadata associated with the resource. +*/ +@interface WebResource : NSObject <NSCoding, NSCopying> +{ +@private + WebResourcePrivate *_private; +} + +/*! + @method initWithData:URL:MIMEType:textEncodingName:frameName + @abstract The initializer for WebResource. + @param data The data of the resource. + @param URL The URL of the resource. + @param MIMEType The MIME type of the resource. + @param textEncodingName The text encoding name of the resource (can be nil). + @param frameName The frame name of the resource if the resource represents the contents of an entire HTML frame (can be nil). + @result An initialized WebResource. +*/ +- (id)initWithData:(NSData *)data URL:(NSURL *)URL MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName frameName:(NSString *)frameName; + +/*! + @method data + @result The data of the resource. +*/ +- (NSData *)data; + +/*! + @method URL + @result The URL of the resource. +*/ +- (NSURL *)URL; + +/*! + @method MIMEType + @result The MIME type of the resource. +*/ +- (NSString *)MIMEType; + +/*! + @method textEncodingName + @result The text encoding name of the resource (can be nil). +*/ +- (NSString *)textEncodingName; + +/*! + @method frameName + @result The frame name of the resource if the resource represents the contents of an entire HTML frame (can be nil). +*/ +- (NSString *)frameName; + +@end diff --git a/Source/WebKit/mac/WebView/WebResource.mm b/Source/WebKit/mac/WebView/WebResource.mm new file mode 100644 index 0000000..fd02212 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebResource.mm @@ -0,0 +1,453 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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 "WebResourceInternal.h" + +#import "WebFrameInternal.h" +#import "WebKitLogging.h" +#import "WebKitVersionChecks.h" +#import "WebNSDictionaryExtras.h" +#import "WebNSObjectExtras.h" +#import "WebNSURLExtras.h" +#import <JavaScriptCore/InitializeThreading.h> +#import <JavaScriptCore/PassRefPtr.h> +#import <WebCore/ArchiveResource.h> +#import <WebCore/LegacyWebArchive.h> +#import <WebCore/RuntimeApplicationChecks.h> +#import <WebCore/TextEncoding.h> +#import <WebCore/ThreadCheck.h> +#import <WebCore/WebCoreObjCExtras.h> +#import <WebCore/WebCoreURLResponse.h> +#import <wtf/Threading.h> + +using namespace WebCore; + +static NSString * const WebResourceDataKey = @"WebResourceData"; +static NSString * const WebResourceFrameNameKey = @"WebResourceFrameName"; +static NSString * const WebResourceMIMETypeKey = @"WebResourceMIMEType"; +static NSString * const WebResourceURLKey = @"WebResourceURL"; +static NSString * const WebResourceTextEncodingNameKey = @"WebResourceTextEncodingName"; +static NSString * const WebResourceResponseKey = @"WebResourceResponse"; + +@interface WebResourcePrivate : NSObject { +@public + ArchiveResource* coreResource; +} +- (id)initWithCoreResource:(PassRefPtr<ArchiveResource>)coreResource; +@end + +@implementation WebResourcePrivate + ++ (void)initialize +{ + JSC::initializeThreading(); + WTF::initializeMainThreadToProcessMainThread(); +#ifndef BUILDING_ON_TIGER + WebCoreObjCFinalizeOnMainThread(self); +#endif +} + +- (id)init +{ + return [super init]; +} + +- (id)initWithCoreResource:(PassRefPtr<ArchiveResource>)passedResource +{ + self = [super init]; + if (!self) + return nil; + // Acquire the PassRefPtr<>'s ref as our own manual ref + coreResource = passedResource.releaseRef(); + return self; +} + +- (void)dealloc +{ + if (WebCoreObjCScheduleDeallocateOnMainThread([WebResourcePrivate class], self)) + return; + + if (coreResource) + coreResource->deref(); + [super dealloc]; +} + +- (void)finalize +{ + if (coreResource) + coreResource->deref(); + [super finalize]; +} + +@end + +@implementation WebResource + +- (id)init +{ + self = [super init]; + if (!self) + return nil; + _private = [[WebResourcePrivate alloc] init]; + return self; +} + +- (id)initWithData:(NSData *)data URL:(NSURL *)URL MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName frameName:(NSString *)frameName +{ + return [self _initWithData:data URL:URL MIMEType:MIMEType textEncodingName:textEncodingName frameName:frameName response:nil copyData:YES]; +} + +- (id)initWithCoder:(NSCoder *)decoder +{ + WebCoreThreadViolationCheckRoundTwo(); + + self = [super init]; + if (!self) + return nil; + + NSData *data = nil; + NSURL *url = nil; + NSString *mimeType = nil, *textEncoding = nil, *frameName = nil; + NSURLResponse *response = nil; + + @try { + id object = [decoder decodeObjectForKey:WebResourceDataKey]; + if ([object isKindOfClass:[NSData class]]) + data = object; + object = [decoder decodeObjectForKey:WebResourceURLKey]; + if ([object isKindOfClass:[NSURL class]]) + url = object; + object = [decoder decodeObjectForKey:WebResourceMIMETypeKey]; + if ([object isKindOfClass:[NSString class]]) + mimeType = object; + object = [decoder decodeObjectForKey:WebResourceTextEncodingNameKey]; + if ([object isKindOfClass:[NSString class]]) + textEncoding = object; + object = [decoder decodeObjectForKey:WebResourceFrameNameKey]; + if ([object isKindOfClass:[NSString class]]) + frameName = object; + object = [decoder decodeObjectForKey:WebResourceResponseKey]; + if ([object isKindOfClass:[NSURLResponse class]]) + response = object; + } @catch(id) { + [self release]; + return nil; + } + + _private = [[WebResourcePrivate alloc] initWithCoreResource:ArchiveResource::create(SharedBuffer::wrapNSData(data), url, mimeType, textEncoding, frameName, response)]; + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)encoder +{ + ArchiveResource *resource = _private->coreResource; + + NSData *data = nil; + NSURL *url = nil; + NSString *mimeType = nil, *textEncoding = nil, *frameName = nil; + NSURLResponse *response = nil; + + if (resource) { + if (resource->data()) + data = [resource->data()->createNSData() autorelease]; + url = resource->url(); + mimeType = resource->mimeType(); + textEncoding = resource->textEncoding(); + frameName = resource->frameName(); + response = resource->response().nsURLResponse(); + } + [encoder encodeObject:data forKey:WebResourceDataKey]; + [encoder encodeObject:url forKey:WebResourceURLKey]; + [encoder encodeObject:mimeType forKey:WebResourceMIMETypeKey]; + [encoder encodeObject:textEncoding forKey:WebResourceTextEncodingNameKey]; + [encoder encodeObject:frameName forKey:WebResourceFrameNameKey]; + [encoder encodeObject:response forKey:WebResourceResponseKey]; +} + +- (void)dealloc +{ + [_private release]; + [super dealloc]; +} + +- (id)copyWithZone:(NSZone *)zone +{ + return [self retain]; +} + +- (NSData *)data +{ +#ifdef MAIL_THREAD_WORKAROUND + if (needMailThreadWorkaround()) + return [[self _webkit_invokeOnMainThread] data]; +#endif + + WebCoreThreadViolationCheckRoundTwo(); + + if (!_private->coreResource) + return nil; + if (!_private->coreResource->data()) + return nil; + return [_private->coreResource->data()->createNSData() autorelease]; +} + +- (NSURL *)URL +{ +#ifdef MAIL_THREAD_WORKAROUND + if (needMailThreadWorkaround()) + return [[self _webkit_invokeOnMainThread] URL]; +#endif + + WebCoreThreadViolationCheckRoundTwo(); + + if (!_private->coreResource) + return nil; + NSURL *url = _private->coreResource->url(); + return url; +} + +- (NSString *)MIMEType +{ +#ifdef MAIL_THREAD_WORKAROUND + if (needMailThreadWorkaround()) + return [[self _webkit_invokeOnMainThread] MIMEType]; +#endif + + WebCoreThreadViolationCheckRoundTwo(); + + if (!_private->coreResource) + return nil; + NSString *mimeType = _private->coreResource->mimeType(); + return mimeType; +} + +- (NSString *)textEncodingName +{ +#ifdef MAIL_THREAD_WORKAROUND + if (needMailThreadWorkaround()) + return [[self _webkit_invokeOnMainThread] textEncodingName]; +#endif + + WebCoreThreadViolationCheckRoundTwo(); + + if (!_private->coreResource) + return nil; + NSString *textEncodingName = _private->coreResource->textEncoding(); + return textEncodingName; +} + +- (NSString *)frameName +{ +#ifdef MAIL_THREAD_WORKAROUND + if (needMailThreadWorkaround()) + return [[self _webkit_invokeOnMainThread] frameName]; +#endif + + WebCoreThreadViolationCheckRoundTwo(); + + if (!_private->coreResource) + return nil; + NSString *frameName = _private->coreResource->frameName(); + return frameName; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@ %@>", [self className], [self URL]]; +} + +@end + +@implementation WebResource (WebResourceInternal) + +- (id)_initWithCoreResource:(PassRefPtr<ArchiveResource>)coreResource +{ + self = [super init]; + if (!self) + return nil; + + ASSERT(coreResource); + + // WebResources should not be init'ed with nil data, and doing so breaks certain uses of NSHTMLReader + // See <rdar://problem/5820157> for more info + if (!coreResource->data()) { + [self release]; + return nil; + } + + _private = [[WebResourcePrivate alloc] initWithCoreResource:coreResource]; + + return self; +} + +- (WebCore::ArchiveResource *)_coreResource +{ + return _private->coreResource; +} + +@end + +@implementation WebResource (WebResourcePrivate) + +// SPI for Mail (5066325) +// FIXME: This "ignoreWhenUnarchiving" concept is an ugly one - can we find a cleaner solution for those who need this SPI? +- (void)_ignoreWhenUnarchiving +{ +#ifdef MAIL_THREAD_WORKAROUND + if (needMailThreadWorkaround()) { + [[self _webkit_invokeOnMainThread] _ignoreWhenUnarchiving]; + return; + } +#endif + + WebCoreThreadViolationCheckRoundTwo(); + + if (!_private->coreResource) + return; + _private->coreResource->ignoreWhenUnarchiving(); +} + +- (id)_initWithData:(NSData *)data + URL:(NSURL *)URL + MIMEType:(NSString *)MIMEType + textEncodingName:(NSString *)textEncodingName + frameName:(NSString *)frameName + response:(NSURLResponse *)response + copyData:(BOOL)copyData +{ +#ifdef MAIL_THREAD_WORKAROUND + if (needMailThreadWorkaround()) + return [[self _webkit_invokeOnMainThread] _initWithData:data URL:URL MIMEType:MIMEType textEncodingName:textEncodingName frameName:frameName response:response copyData:copyData]; +#endif + + WebCoreThreadViolationCheckRoundTwo(); + + self = [super init]; + if (!self) + return nil; + + if (!data || !URL || !MIMEType) { + [self release]; + return nil; + } + + _private = [[WebResourcePrivate alloc] initWithCoreResource:ArchiveResource::create(SharedBuffer::wrapNSData(copyData ? [[data copy] autorelease] : data), URL, MIMEType, textEncodingName, frameName, response)]; + + return self; +} + +- (id)_initWithData:(NSData *)data URL:(NSURL *)URL response:(NSURLResponse *)response +{ + // Pass NO for copyData since the data doesn't need to be copied since we know that callers will no longer modify it. + // Copying it will also cause a performance regression. + return [self _initWithData:data + URL:URL + MIMEType:[response MIMEType] + textEncodingName:[response textEncodingName] + frameName:nil + response:response + copyData:NO]; +} + +- (NSString *)_suggestedFilename +{ +#ifdef MAIL_THREAD_WORKAROUND + if (needMailThreadWorkaround()) + return [[self _webkit_invokeOnMainThread] _suggestedFilename]; +#endif + + WebCoreThreadViolationCheckRoundTwo(); + + if (!_private->coreResource) + return nil; + NSString *suggestedFilename = _private->coreResource->response().suggestedFilename(); + return suggestedFilename; +} + +- (NSFileWrapper *)_fileWrapperRepresentation +{ + NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:[self data]] autorelease]; + NSString *filename = [self _suggestedFilename]; + if (!filename || ![filename length]) + filename = [[self URL] _webkit_suggestedFilenameWithMIMEType:[self MIMEType]]; + [wrapper setPreferredFilename:filename]; + return wrapper; +} + +- (NSURLResponse *)_response +{ +#ifdef MAIL_THREAD_WORKAROUND + if (needMailThreadWorkaround()) + return [[self _webkit_invokeOnMainThread] _response]; +#endif + + WebCoreThreadViolationCheckRoundTwo(); + + NSURLResponse *response = nil; + if (_private->coreResource) + response = _private->coreResource->response().nsURLResponse(); + return response ? response : [[[NSURLResponse alloc] init] autorelease]; +} + +- (NSString *)_stringValue +{ +#ifdef MAIL_THREAD_WORKAROUND + if (needMailThreadWorkaround()) + return [[self _webkit_invokeOnMainThread] _stringValue]; +#endif + + WebCoreThreadViolationCheckRoundTwo(); + + WebCore::TextEncoding encoding; + if (_private->coreResource) + encoding = _private->coreResource->textEncoding(); + if (!encoding.isValid()) + encoding = WindowsLatin1Encoding(); + + SharedBuffer* coreData = _private->coreResource ? _private->coreResource->data() : 0; + return encoding.decode(reinterpret_cast<const char*>(coreData ? coreData->data() : 0), coreData ? coreData->size() : 0); +} + +@end + +#ifdef MAIL_THREAD_WORKAROUND + +static const double newMailBundleVersion = 1050.0; + +@implementation WebResource (WebMailThreadWorkaround) + ++ (BOOL)_needMailThreadWorkaroundIfCalledOffMainThread +{ + static BOOL isOldMail = applicationIsAppleMail() && [[[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey] doubleValue] < newMailBundleVersion; + return isOldMail; +} + +@end + +#endif diff --git a/Source/WebKit/mac/WebView/WebResourceInternal.h b/Source/WebKit/mac/WebView/WebResourceInternal.h new file mode 100644 index 0000000..d135197 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebResourceInternal.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * 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 "WebResourcePrivate.h" +#import <wtf/PassRefPtr.h> + +#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) +#define MAIL_THREAD_WORKAROUND 1 +#endif + +namespace WebCore { + class ArchiveResource; +} + +@interface WebResource (WebResourceInternal) +- (id)_initWithCoreResource:(PassRefPtr<WebCore::ArchiveResource>)coreResource; +- (WebCore::ArchiveResource*)_coreResource; +@end + +#ifdef MAIL_THREAD_WORKAROUND + +@interface WebResource (WebMailThreadWorkaround) ++ (BOOL)_needMailThreadWorkaroundIfCalledOffMainThread; +@end + +inline bool needMailThreadWorkaround() +{ + return !pthread_main_np() && [WebResource _needMailThreadWorkaroundIfCalledOffMainThread]; +} + +#endif diff --git a/Source/WebKit/mac/WebView/WebResourceLoadDelegate.h b/Source/WebKit/mac/WebView/WebResourceLoadDelegate.h new file mode 100644 index 0000000..77d84ad --- /dev/null +++ b/Source/WebKit/mac/WebView/WebResourceLoadDelegate.h @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2003, 2004, 2005 Apple Computer, Inc. All rights reserved. + * + * 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. + */ + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 +#define WebNSInteger int +#else +#define WebNSInteger NSInteger +#endif + +@class WebView; +@class WebDataSource; +@class NSURLAuthenticationChallenge; +@class NSURLResponse; +@class NSURLRequest; + +/*! + @category WebResourceLoadDelegate + @discussion Implementors of this protocol will receive messages indicating + that a resource is about to be loaded, data has been received for a resource, + an error has been received for a resource, and completion of a resource load. + Implementors are also given the opportunity to mutate requests before they are sent. + The various progress methods of this protocol all receive an identifier as the + parameter. This identifier can be used to track messages associated with a single + resource. For example, a single resource may generate multiple + resource:willSendRequest:redirectResponse:fromDataSource: messages as it's URL is redirected. +*/ +@interface NSObject (WebResourceLoadDelegate) + +/*! + @method webView:identifierForInitialRequest:fromDataSource: + @param webView The WebView sending the message. + @param request The request about to be sent. + @param dataSource The datasource that initiated the load. + @discussion An implementor of WebResourceLoadDelegate should provide an identifier + that can be used to track the load of a single resource. This identifier will be + passed as the first argument for all of the other WebResourceLoadDelegate methods. The + identifier is useful to track changes to a resources request, which will be + provided by one or more calls to resource:willSendRequest:redirectResponse:fromDataSource:. + @result An identifier that will be passed back to the implementor for each callback. + The identifier will be retained. +*/ +- (id)webView:(WebView *)sender identifierForInitialRequest:(NSURLRequest *)request fromDataSource:(WebDataSource *)dataSource; + +/*! + @method webView:resource:willSendRequest:redirectResponse:fromDataSource: + @discussion This message is sent before a load is initiated. The request may be modified + as necessary by the receiver. + @param webView The WebView sending the message. + @param identifier An identifier that can be used to track the progress of a resource load across + multiple call backs. + @param request The request about to be sent. + @param redirectResponse If the request is being made in response to a redirect we received, + the response that conveyed that redirect. + @param dataSource The dataSource that initiated the load. + @result Returns the request, which may be mutated by the implementor, although typically + will be request. +*/ +- (NSURLRequest *)webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource; + +/*! + @method webView:resource:didReceiveAuthenticationChallenge:fromDataSource: + @abstract Start authentication for the resource, providing a challenge + @discussion Call useCredential::, continueWithoutCredential or + cancel on the challenge when done. + @param challenge The NSURLAuthenticationChallenge to start authentication for + @discussion If you do not implement this delegate method, WebKit will handle authentication + automatically by prompting with a sheet on the window that the WebView is associated with. +*/ +- (void)webView:(WebView *)sender resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)dataSource; + +/*! + @method webView:resource:didCancelAuthenticationChallenge:fromDataSource: + @abstract Cancel authentication for a given request + @param challenge The NSURLAuthenticationChallenge for which to cancel authentication +*/ +- (void)webView:(WebView *)sender resource:(id)identifier didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)dataSource; + +/*! + @method webView:resource:didReceiveResponse:fromDataSource: + @abstract This message is sent after a response has been received for this load. + @param webView The WebView sending the message. + @param identifier An identifier that can be used to track the progress of a resource load across + multiple call backs. + @param response The response for the request. + @param dataSource The dataSource that initiated the load. + @discussion In some rare cases, multiple responses may be received for a single load. + This occurs with multipart/x-mixed-replace, or "server push". In this case, the client + should assume that each new response resets progress so far for the resource back to 0, + and should check the new response for the expected content length. +*/ +- (void)webView:(WebView *)sender resource:(id)identifier didReceiveResponse:(NSURLResponse *)response fromDataSource:(WebDataSource *)dataSource; + +/*! + @method webView:resource:didReceiveContentLength:fromDataSource: + @discussion Multiple of these messages may be sent as data arrives. + @param webView The WebView sending the message. + @param identifier An identifier that can be used to track the progress of a resource load across + multiple call backs. + @param length The amount of new data received. This is not the total amount, just the new amount received. + @param dataSource The dataSource that initiated the load. +*/ +- (void)webView:(WebView *)sender resource:(id)identifier didReceiveContentLength:(WebNSInteger)length fromDataSource:(WebDataSource *)dataSource; + +/*! + @method webView:resource:didFinishLoadingFromDataSource: + @discussion This message is sent after a load has successfully completed. + @param webView The WebView sending the message. + @param identifier An identifier that can be used to track the progress of a resource load across + multiple call backs. + @param dataSource The dataSource that initiated the load. +*/ +- (void)webView:(WebView *)sender resource:(id)identifier didFinishLoadingFromDataSource:(WebDataSource *)dataSource; + +/*! + @method webView:resource:didFailLoadingWithError:fromDataSource: + @discussion This message is sent after a load has failed to load due to an error. + @param webView The WebView sending the message. + @param identifier An identifier that can be used to track the progress of a resource load across + multiple call backs. + @param error The error associated with this load. + @param dataSource The dataSource that initiated the load. +*/ +- (void)webView:(WebView *)sender resource:(id)identifier didFailLoadingWithError:(NSError *)error fromDataSource:(WebDataSource *)dataSource; + +/*! + @method webView:plugInFailedWithError:dataSource: + @discussion Called when a plug-in is not found, fails to load or is not available for some reason. + @param webView The WebView sending the message. + @param error The plug-in error. In the userInfo dictionary of the error, the object for the + NSErrorFailingURLKey key is a URL string of the SRC attribute, the object for the WebKitErrorPlugInNameKey + key is a string of the plug-in's name, the object for the WebKitErrorPlugInPageURLStringKey key is a URL string + of the PLUGINSPAGE attribute and the object for the WebKitErrorMIMETypeKey key is a string of the TYPE attribute. + Some, none or all of the mentioned attributes can be present in the userInfo. The error returns nil for userInfo + when none are present. + @param dataSource The dataSource that contains the plug-in. +*/ +- (void)webView:(WebView *)sender plugInFailedWithError:(NSError *)error dataSource:(WebDataSource *)dataSource; + +@end + +#undef WebNSInteger diff --git a/Source/WebKit/mac/WebView/WebResourceLoadDelegatePrivate.h b/Source/WebKit/mac/WebView/WebResourceLoadDelegatePrivate.h new file mode 100644 index 0000000..45d9c09 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebResourceLoadDelegatePrivate.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * 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. + */ + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 +#define WebNSInteger int +#else +#define WebNSInteger NSInteger +#endif + +@class WebView; +@class WebDataSource; +@class NSURLAuthenticationChallenge; +@class NSURLResponse; +@class NSURLRequest; + +@interface NSObject (WebResourceLoadDelegatePrivate) + +- (void)webView:(WebView *)webView didLoadResourceFromMemoryCache:(NSURLRequest *)request response:(NSURLResponse *)response length:(WebNSInteger)length fromDataSource:(WebDataSource *)dataSource; +- (BOOL)webView:(WebView *)webView resource:(id)identifier shouldUseCredentialStorageForDataSource:(WebDataSource *)dataSource; + +#if WEBKIT_VERSION_MAX_ALLOWED > WEBKIT_VERSION_4_0 +/*! + @method webView:resource:canAuthenticateAgainstProtectionSpace:forDataSource: + @abstract Inspect an NSURLProtectionSpace before an authentication attempt is made. Only used on Snow Leopard or newer. + @param protectionSpace an NSURLProtectionSpace that will be used to generate an authentication challenge + @result Return YES if the resource load delegate is prepared to respond to an authentication challenge generated with protectionSpace, NO otherwise + */ +- (BOOL)webView:(WebView *)sender resource:(id)identifier canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace forDataSource:(WebDataSource *)dataSource; +#endif + +@end + +#undef WebNSInteger diff --git a/Source/WebKit/mac/WebView/WebResourcePrivate.h b/Source/WebKit/mac/WebView/WebResourcePrivate.h new file mode 100644 index 0000000..80d9835 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebResourcePrivate.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * 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 <WebKit/WebResource.h> + +@interface WebResource (WebResourcePrivate) + +- (id)_initWithData:(NSData *)data + URL:(NSURL *)URL + MIMEType:(NSString *)MIMEType + textEncodingName:(NSString *)textEncodingName + frameName:(NSString *)frameName + response:(NSURLResponse *)response + copyData:(BOOL)copyData; + +- (id)_initWithData:(NSData *)data URL:(NSURL *)URL response:(NSURLResponse *)response; + +- (void)_ignoreWhenUnarchiving; + +- (NSFileWrapper *)_fileWrapperRepresentation; +- (NSURLResponse *)_response; +- (NSString *)_stringValue; + +@end diff --git a/Source/WebKit/mac/WebView/WebScriptDebugDelegate.h b/Source/WebKit/mac/WebView/WebScriptDebugDelegate.h new file mode 100644 index 0000000..823cc35 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebScriptDebugDelegate.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * 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 <Foundation/Foundation.h> + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 +#define WebNSUInteger unsigned int +#else +#define WebNSUInteger NSUInteger +#endif + +#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) +typedef int WebSourceId; +#else +typedef intptr_t WebSourceId; +#endif + +@class WebView; +@class WebFrame; +@class WebScriptCallFrame; +@class WebScriptCallFramePrivate; +@class WebScriptObject; + +extern NSString * const WebScriptErrorDomain; +extern NSString * const WebScriptErrorDescriptionKey; +extern NSString * const WebScriptErrorLineNumberKey; + +enum { + WebScriptGeneralErrorCode = -100 +}; + +// WebScriptDebugDelegate messages + +@interface NSObject (WebScriptDebugDelegate) + +// some source was parsed, establishing a "source ID" (>= 0) for future reference +// this delegate method is deprecated, please switch to the new version below +- (void)webView:(WebView *)webView didParseSource:(NSString *)source + fromURL:(NSString *)url + sourceId:(WebSourceId)sid + forWebFrame:(WebFrame *)webFrame; + +// some source was parsed, establishing a "source ID" (>= 0) for future reference +- (void)webView:(WebView *)webView didParseSource:(NSString *)source + baseLineNumber:(WebNSUInteger)lineNumber + fromURL:(NSURL *)url + sourceId:(WebSourceId)sid + forWebFrame:(WebFrame *)webFrame; + +// some source failed to parse +- (void)webView:(WebView *)webView failedToParseSource:(NSString *)source + baseLineNumber:(WebNSUInteger)lineNumber + fromURL:(NSURL *)url + withError:(NSError *)error + forWebFrame:(WebFrame *)webFrame; + +// just entered a stack frame (i.e. called a function, or started global scope) +- (void)webView:(WebView *)webView didEnterCallFrame:(WebScriptCallFrame *)frame + sourceId:(WebSourceId)sid + line:(int)lineno + forWebFrame:(WebFrame *)webFrame; + +// about to execute some code +- (void)webView:(WebView *)webView willExecuteStatement:(WebScriptCallFrame *)frame + sourceId:(WebSourceId)sid + line:(int)lineno + forWebFrame:(WebFrame *)webFrame; + +// about to leave a stack frame (i.e. return from a function) +- (void)webView:(WebView *)webView willLeaveCallFrame:(WebScriptCallFrame *)frame + sourceId:(WebSourceId)sid + line:(int)lineno + forWebFrame:(WebFrame *)webFrame; + +// exception is being thrown +- (void)webView:(WebView *)webView exceptionWasRaised:(WebScriptCallFrame *)frame + sourceId:(WebSourceId)sid + line:(int)lineno + forWebFrame:(WebFrame *)webFrame; +@end + + + +// WebScriptCallFrame interface +// +// These objects are passed as arguments to the debug delegate. + +@interface WebScriptCallFrame : NSObject +{ +@private + WebScriptCallFramePrivate* _private; + id _userInfo; +} + +// associate user info with frame +- (void)setUserInfo:(id)userInfo; + +// retrieve user info +- (id)userInfo; + +// get next frame on call stack (or nil if this is already the "global" frame) +- (WebScriptCallFrame *)caller; + +// get array of WebScriptObjects for each scope (innermost first, last is always global object) +- (NSArray *)scopeChain; + +// get name of function (if available) or nil +- (NSString *)functionName; + +// get pending exception (if any) or nil +- (id)exception; + +// evaluate a script (as if by "eval") in the context of this frame +- (id)evaluateWebScript:(NSString *)script; + +@end + +#undef WebNSUInteger + diff --git a/Source/WebKit/mac/WebView/WebScriptDebugDelegate.mm b/Source/WebKit/mac/WebView/WebScriptDebugDelegate.mm new file mode 100644 index 0000000..63a91a9 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebScriptDebugDelegate.mm @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * 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 "WebDataSource.h" +#import "WebDataSourceInternal.h" +#import "WebFrameInternal.h" +#import "WebScriptDebugDelegate.h" +#import "WebScriptDebugger.h" +#import "WebViewInternal.h" +#import <WebCore/Frame.h> +#import <WebCore/ScriptController.h> +#import <WebCore/WebScriptObjectPrivate.h> +#import <WebCore/runtime_root.h> +#import <debugger/Debugger.h> +#import <debugger/DebuggerActivation.h> +#import <debugger/DebuggerCallFrame.h> +#import <interpreter/CallFrame.h> +#import <runtime/Completion.h> +#import <runtime/JSFunction.h> +#import <runtime/JSGlobalObject.h> +#import <runtime/JSLock.h> + +using namespace JSC; +using namespace WebCore; + +// FIXME: these error strings should be public for future use by WebScriptObject and in WebScriptObject.h +NSString * const WebScriptErrorDomain = @"WebScriptErrorDomain"; +NSString * const WebScriptErrorDescriptionKey = @"WebScriptErrorDescription"; +NSString * const WebScriptErrorLineNumberKey = @"WebScriptErrorLineNumber"; + +@interface WebScriptCallFrame (WebScriptDebugDelegateInternal) + +- (id)_convertValueToObjcValue:(JSValue)value; + +@end + +@interface WebScriptCallFramePrivate : NSObject { +@public + WebScriptObject *globalObject; // the global object's proxy (not retained) + WebScriptCallFrame *caller; // previous stack frame + DebuggerCallFrame* debuggerCallFrame; + WebScriptDebugger* debugger; +} +@end + +@implementation WebScriptCallFramePrivate +- (void)dealloc +{ + [caller release]; + delete debuggerCallFrame; + [super dealloc]; +} +@end + +// WebScriptCallFrame +// +// One of these is created to represent each stack frame. Additionally, there is a "global" +// frame to represent the outermost scope. This global frame is always the last frame in +// the chain of callers. +// +// The delegate can assign a "wrapper" to each frame object so it can relay calls through its +// own exported interface. This class is private to WebCore (and the delegate). + +@implementation WebScriptCallFrame (WebScriptDebugDelegateInternal) + +- (WebScriptCallFrame *)_initWithGlobalObject:(WebScriptObject *)globalObj debugger:(WebScriptDebugger *)debugger caller:(WebScriptCallFrame *)caller debuggerCallFrame:(const DebuggerCallFrame&)debuggerCallFrame +{ + if ((self = [super init])) { + _private = [[WebScriptCallFramePrivate alloc] init]; + _private->globalObject = globalObj; + _private->caller = [caller retain]; + _private->debugger = debugger; + } + return self; +} + +- (void)_setDebuggerCallFrame:(const DebuggerCallFrame&)debuggerCallFrame +{ + if (!_private->debuggerCallFrame) + _private->debuggerCallFrame = new DebuggerCallFrame(debuggerCallFrame); + else + *_private->debuggerCallFrame = debuggerCallFrame; +} + +- (void)_clearDebuggerCallFrame +{ + delete _private->debuggerCallFrame; + _private->debuggerCallFrame = 0; +} + +- (id)_convertValueToObjcValue:(JSValue)value +{ + if (!value) + return nil; + + WebScriptObject *globalObject = _private->globalObject; + if (value == [globalObject _imp]) + return globalObject; + + Bindings::RootObject* root1 = [globalObject _originRootObject]; + if (!root1) + return nil; + + Bindings::RootObject* root2 = [globalObject _rootObject]; + if (!root2) + return nil; + + return [WebScriptObject _convertValueToObjcValue:value originRootObject:root1 rootObject:root2]; +} + +@end + + + +@implementation WebScriptCallFrame + +- (void) dealloc +{ + [_userInfo release]; + [_private release]; + [super dealloc]; +} + +- (void)setUserInfo:(id)userInfo +{ + if (userInfo != _userInfo) { + [_userInfo release]; + _userInfo = [userInfo retain]; + } +} + +- (id)userInfo +{ + return _userInfo; +} + +- (WebScriptCallFrame *)caller +{ + return _private->caller; +} + +// Returns an array of scope objects (most local first). +// The properties of each scope object are the variables for that scope. +// Note that the last entry in the array will _always_ be the global object (windowScriptObject), +// whose properties are the global variables. + +- (NSArray *)scopeChain +{ + if (!_private->debuggerCallFrame) + return [NSArray array]; + + JSLock lock(SilenceAssertionsOnly); + + const ScopeChainNode* scopeChain = _private->debuggerCallFrame->scopeChain(); + if (!scopeChain->next) // global frame + return [NSArray arrayWithObject:_private->globalObject]; + + NSMutableArray *scopes = [[NSMutableArray alloc] init]; + + ScopeChainIterator end = scopeChain->end(); + for (ScopeChainIterator it = scopeChain->begin(); it != end; ++it) { + JSObject* object = *it; + if (object->isActivationObject()) + object = new (scopeChain->globalData) DebuggerActivation(object); + [scopes addObject:[self _convertValueToObjcValue:object]]; + } + + NSArray *result = [NSArray arrayWithArray:scopes]; + [scopes release]; + return result; +} + +// Returns the name of the function for this frame, if available. +// Returns nil for anonymous functions and for the global frame. + +- (NSString *)functionName +{ + if (!_private->debuggerCallFrame) + return nil; + + const UString* functionName = _private->debuggerCallFrame->functionName(); + return functionName ? toNSString(*functionName) : nil; +} + +// Returns the pending exception for this frame (nil if none). + +- (id)exception +{ + if (!_private->debuggerCallFrame) + return nil; + + JSValue exception = _private->debuggerCallFrame->exception(); + return exception ? [self _convertValueToObjcValue:exception] : nil; +} + +// Evaluate some JavaScript code in the context of this frame. +// The code is evaluated as if by "eval", and the result is returned. +// If there is an (uncaught) exception, it is returned as though _it_ were the result. +// Calling this method on the global frame is not quite the same as calling the WebScriptObject +// method of the same name, due to the treatment of exceptions. + +- (id)evaluateWebScript:(NSString *)script +{ + if (!_private->debuggerCallFrame) + return nil; + + JSLock lock(SilenceAssertionsOnly); + + // If this is the global call frame and there is no dynamic global object, + // Dashcode is attempting to execute JS in the evaluator using a stale + // WebScriptCallFrame. Instead, we need to set the dynamic global object + // and evaluate the JS in the global object's global call frame. + JSGlobalObject* globalObject = _private->debugger->globalObject(); + if (self == _private->debugger->globalCallFrame() && !globalObject->globalData().dynamicGlobalObject) { + JSGlobalObject* globalObject = _private->debugger->globalObject(); + + DynamicGlobalObjectScope globalObjectScope(globalObject->globalExec(), globalObject); + + JSValue exception; + JSValue result = evaluateInGlobalCallFrame(stringToUString(script), exception, globalObject); + if (exception) + return [self _convertValueToObjcValue:exception]; + return result ? [self _convertValueToObjcValue:result] : nil; + } + + JSValue exception; + JSValue result = _private->debuggerCallFrame->evaluate(stringToUString(script), exception); + if (exception) + return [self _convertValueToObjcValue:exception]; + return result ? [self _convertValueToObjcValue:result] : nil; +} + +@end diff --git a/Source/WebKit/mac/WebView/WebScriptDebugger.h b/Source/WebKit/mac/WebView/WebScriptDebugger.h new file mode 100644 index 0000000..c4147a2 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebScriptDebugger.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * 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. + */ + +#ifndef WebScriptDebugger_h +#define WebScriptDebugger_h + +#include <debugger/Debugger.h> +#include <runtime/Protect.h> + +#include <wtf/RetainPtr.h> + +namespace JSC { + class DebuggerCallFrame; + class ExecState; + class JSGlobalObject; + class JSObject; + class ArgList; + class UString; +} + +@class WebScriptCallFrame; + +NSString *toNSString(const JSC::UString&); + +class WebScriptDebugger : public JSC::Debugger { +public: + WebScriptDebugger(JSC::JSGlobalObject*); + + void initGlobalCallFrame(const JSC::DebuggerCallFrame&); + + virtual void sourceParsed(JSC::ExecState*, const JSC::SourceCode&, int errorLine, const JSC::UString& errorMsg); + virtual void callEvent(const JSC::DebuggerCallFrame&, intptr_t sourceID, int lineNumber); + virtual void atStatement(const JSC::DebuggerCallFrame&, intptr_t sourceID, int lineNumber); + virtual void returnEvent(const JSC::DebuggerCallFrame&, intptr_t sourceID, int lineNumber); + virtual void exception(const JSC::DebuggerCallFrame&, intptr_t sourceID, int lineNumber, bool hasHandler); + virtual void willExecuteProgram(const JSC::DebuggerCallFrame&, intptr_t sourceID, int lineno); + virtual void didExecuteProgram(const JSC::DebuggerCallFrame&, intptr_t sourceID, int lineno); + virtual void didReachBreakpoint(const JSC::DebuggerCallFrame&, intptr_t sourceID, int lineno); + + JSC::JSGlobalObject* globalObject() const { return m_globalObject.get(); } + WebScriptCallFrame *globalCallFrame() const { return m_globalCallFrame.get(); } + +private: + bool m_callingDelegate; + RetainPtr<WebScriptCallFrame> m_topCallFrame; + + JSC::ProtectedPtr<JSC::JSGlobalObject> m_globalObject; + RetainPtr<WebScriptCallFrame> m_globalCallFrame; +}; + +#endif // WebScriptDebugger_h diff --git a/Source/WebKit/mac/WebView/WebScriptDebugger.mm b/Source/WebKit/mac/WebView/WebScriptDebugger.mm new file mode 100644 index 0000000..25ef4b7 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebScriptDebugger.mm @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * 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 "WebScriptDebugger.h" + +#import "WebDelegateImplementationCaching.h" +#import "WebFrameInternal.h" +#import "WebScriptDebugDelegate.h" +#import "WebViewInternal.h" +#import <JavaScriptCore/DebuggerCallFrame.h> +#import <JavaScriptCore/JSGlobalObject.h> +#import <JavaScriptCore/SourceCode.h> +#import <WebCore/DOMWindow.h> +#import <WebCore/Frame.h> +#import <WebCore/JSDOMWindow.h> +#import <WebCore/KURL.h> +#import <WebCore/ScriptController.h> + +using namespace JSC; +using namespace WebCore; + +@interface WebScriptCallFrame (WebScriptDebugDelegateInternal) +- (WebScriptCallFrame *)_initWithGlobalObject:(WebScriptObject *)globalObj debugger:(WebScriptDebugger *)debugger caller:(WebScriptCallFrame *)caller debuggerCallFrame:(const DebuggerCallFrame&)debuggerCallFrame; +- (void)_setDebuggerCallFrame:(const DebuggerCallFrame&)debuggerCallFrame; +- (void)_clearDebuggerCallFrame; +@end + +NSString *toNSString(const UString& s) +{ + if (s.isEmpty()) + return nil; + return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(s.characters()) length:s.length()]; +} + +static NSString *toNSString(const SourceCode& s) +{ + if (!s.length()) + return nil; + return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(s.data()) length:s.length()]; +} + +// convert UString to NSURL +static NSURL *toNSURL(const UString& s) +{ + if (s.isEmpty()) + return nil; + return KURL(ParsedURLString, ustringToString(s)); +} + +static WebFrame *toWebFrame(JSGlobalObject* globalObject) +{ + JSDOMWindow* window = static_cast<JSDOMWindow*>(globalObject); + return kit(window->impl()->frame()); +} + +WebScriptDebugger::WebScriptDebugger(JSGlobalObject* globalObject) + : m_callingDelegate(false) + , m_globalObject(globalObject) +{ + attach(globalObject); + initGlobalCallFrame(globalObject->globalExec()); +} + +void WebScriptDebugger::initGlobalCallFrame(const DebuggerCallFrame& debuggerCallFrame) +{ + m_callingDelegate = true; + + WebFrame *webFrame = toWebFrame(debuggerCallFrame.dynamicGlobalObject()); + + m_topCallFrame.adoptNS([[WebScriptCallFrame alloc] _initWithGlobalObject:core(webFrame)->script()->windowScriptObject() debugger:this caller:m_topCallFrame.get() debuggerCallFrame:debuggerCallFrame]); + m_globalCallFrame = m_topCallFrame; + + WebView *webView = [webFrame webView]; + WebScriptDebugDelegateImplementationCache* implementations = WebViewGetScriptDebugDelegateImplementations(webView); + if (implementations->didEnterCallFrameFunc) + CallScriptDebugDelegate(implementations->didEnterCallFrameFunc, webView, @selector(webView:didEnterCallFrame:sourceId:line:forWebFrame:), m_topCallFrame.get(), static_cast<NSInteger>(0), -1, webFrame); + + m_callingDelegate = false; +} + +// callbacks - relay to delegate +void WebScriptDebugger::sourceParsed(ExecState* exec, const SourceCode& source, int errorLine, const UString& errorMsg) +{ + if (m_callingDelegate) + return; + + m_callingDelegate = true; + + NSString *nsSource = toNSString(source); + NSURL *nsURL = toNSURL(source.provider()->url()); + + WebFrame *webFrame = toWebFrame(exec->dynamicGlobalObject()); + WebView *webView = [webFrame webView]; + WebScriptDebugDelegateImplementationCache* implementations = WebViewGetScriptDebugDelegateImplementations(webView); + + if (errorLine == -1) { + if (implementations->didParseSourceFunc) { + if (implementations->didParseSourceExpectsBaseLineNumber) + CallScriptDebugDelegate(implementations->didParseSourceFunc, webView, @selector(webView:didParseSource:baseLineNumber:fromURL:sourceId:forWebFrame:), nsSource, source.firstLine(), nsURL, source.provider()->asID(), webFrame); + else + CallScriptDebugDelegate(implementations->didParseSourceFunc, webView, @selector(webView:didParseSource:fromURL:sourceId:forWebFrame:), nsSource, [nsURL absoluteString], source.provider()->asID(), webFrame); + } + } else { + NSString* nsErrorMessage = toNSString(errorMsg); + NSDictionary *info = [[NSDictionary alloc] initWithObjectsAndKeys:nsErrorMessage, WebScriptErrorDescriptionKey, [NSNumber numberWithUnsignedInt:errorLine], WebScriptErrorLineNumberKey, nil]; + NSError *error = [[NSError alloc] initWithDomain:WebScriptErrorDomain code:WebScriptGeneralErrorCode userInfo:info]; + + if (implementations->failedToParseSourceFunc) + CallScriptDebugDelegate(implementations->failedToParseSourceFunc, webView, @selector(webView:failedToParseSource:baseLineNumber:fromURL:withError:forWebFrame:), nsSource, source.firstLine(), nsURL, error, webFrame); + + [error release]; + [info release]; + } + + m_callingDelegate = false; +} + +void WebScriptDebugger::callEvent(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber) +{ + if (m_callingDelegate) + return; + + m_callingDelegate = true; + + WebFrame *webFrame = toWebFrame(debuggerCallFrame.dynamicGlobalObject()); + + m_topCallFrame.adoptNS([[WebScriptCallFrame alloc] _initWithGlobalObject:core(webFrame)->script()->windowScriptObject() debugger:this caller:m_topCallFrame.get() debuggerCallFrame:debuggerCallFrame]); + + WebView *webView = [webFrame webView]; + WebScriptDebugDelegateImplementationCache* implementations = WebViewGetScriptDebugDelegateImplementations(webView); + if (implementations->didEnterCallFrameFunc) + CallScriptDebugDelegate(implementations->didEnterCallFrameFunc, webView, @selector(webView:didEnterCallFrame:sourceId:line:forWebFrame:), m_topCallFrame.get(), sourceID, lineNumber, webFrame); + + m_callingDelegate = false; +} + +void WebScriptDebugger::atStatement(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber) +{ + if (m_callingDelegate) + return; + + m_callingDelegate = true; + + WebFrame *webFrame = toWebFrame(debuggerCallFrame.dynamicGlobalObject()); + WebView *webView = [webFrame webView]; + + [m_topCallFrame.get() _setDebuggerCallFrame:debuggerCallFrame]; + + WebScriptDebugDelegateImplementationCache* implementations = WebViewGetScriptDebugDelegateImplementations(webView); + if (implementations->willExecuteStatementFunc) + CallScriptDebugDelegate(implementations->willExecuteStatementFunc, webView, @selector(webView:willExecuteStatement:sourceId:line:forWebFrame:), m_topCallFrame.get(), sourceID, lineNumber, webFrame); + + m_callingDelegate = false; +} + +void WebScriptDebugger::returnEvent(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber) +{ + if (m_callingDelegate) + return; + + m_callingDelegate = true; + + WebFrame *webFrame = toWebFrame(debuggerCallFrame.dynamicGlobalObject()); + WebView *webView = [webFrame webView]; + + [m_topCallFrame.get() _setDebuggerCallFrame:debuggerCallFrame]; + + WebScriptDebugDelegateImplementationCache* implementations = WebViewGetScriptDebugDelegateImplementations(webView); + if (implementations->willLeaveCallFrameFunc) + CallScriptDebugDelegate(implementations->willLeaveCallFrameFunc, webView, @selector(webView:willLeaveCallFrame:sourceId:line:forWebFrame:), m_topCallFrame.get(), sourceID, lineNumber, webFrame); + + [m_topCallFrame.get() _clearDebuggerCallFrame]; + m_topCallFrame = [m_topCallFrame.get() caller]; + + m_callingDelegate = false; +} + +void WebScriptDebugger::exception(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineNumber, bool hasHandler) +{ + if (m_callingDelegate) + return; + + m_callingDelegate = true; + + WebFrame *webFrame = toWebFrame(debuggerCallFrame.dynamicGlobalObject()); + WebView *webView = [webFrame webView]; + [m_topCallFrame.get() _setDebuggerCallFrame:debuggerCallFrame]; + + WebScriptDebugDelegateImplementationCache* implementations = WebViewGetScriptDebugDelegateImplementations(webView); + if (implementations->exceptionWasRaisedFunc) + CallScriptDebugDelegate(implementations->exceptionWasRaisedFunc, webView, @selector(webView:exceptionWasRaised:sourceId:line:forWebFrame:), m_topCallFrame.get(), sourceID, lineNumber, webFrame); + + m_callingDelegate = false; +} + +void WebScriptDebugger::willExecuteProgram(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineno) +{ + callEvent(debuggerCallFrame, sourceID, lineno); +} + +void WebScriptDebugger::didExecuteProgram(const DebuggerCallFrame& debuggerCallFrame, intptr_t sourceID, int lineno) +{ + returnEvent(debuggerCallFrame, sourceID, lineno); +} + +void WebScriptDebugger::didReachBreakpoint(const DebuggerCallFrame&, intptr_t, int) +{ + return; +} diff --git a/Source/WebKit/mac/WebView/WebScriptWorld.h b/Source/WebKit/mac/WebView/WebScriptWorld.h new file mode 100644 index 0000000..9a05f7f --- /dev/null +++ b/Source/WebKit/mac/WebView/WebScriptWorld.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +typedef struct OpaqueJSContext* JSGlobalContextRef; + +@class WebScriptWorldPrivate; + +@interface WebScriptWorld : NSObject { +@private + WebScriptWorldPrivate *_private; +} + ++ (WebScriptWorld *)standardWorld; ++ (WebScriptWorld *)world; + ++ (WebScriptWorld *)scriptWorldForGlobalContext:(JSGlobalContextRef)globalContext; + +- (void)unregisterWorld; + +@end diff --git a/Source/WebKit/mac/WebView/WebScriptWorld.mm b/Source/WebKit/mac/WebView/WebScriptWorld.mm new file mode 100644 index 0000000..61daff1 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebScriptWorld.mm @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "WebScriptWorld.h" + +#import "WebScriptWorldInternal.h" +#import <WebCore/JSDOMBinding.h> +#import <WebCore/ScriptController.h> +#import <JavaScriptCore/APICast.h> + +#import <wtf/RefPtr.h> + +using namespace WebCore; + +@interface WebScriptWorldPrivate : NSObject { +@public + RefPtr<DOMWrapperWorld> world; +} +@end + +@implementation WebScriptWorldPrivate +@end + +typedef HashMap<DOMWrapperWorld*, WebScriptWorld*> WorldMap; +static WorldMap& allWorlds() +{ + static WorldMap& map = *new WorldMap; + return map; +} + +@implementation WebScriptWorld + +- (id)initWithWorld:(PassRefPtr<DOMWrapperWorld>)world +{ + ASSERT_ARG(world, world); + if (!world) + return nil; + + self = [super init]; + if (!self) + return nil; + + _private = [[WebScriptWorldPrivate alloc] init]; + _private->world = world; + + ASSERT_ARG(world, !allWorlds().contains(_private->world.get())); + allWorlds().add(_private->world.get(), self); + + return self; +} + +- (id)init +{ + return [self initWithWorld:ScriptController::createWorld()]; +} + +- (void)unregisterWorld +{ + _private->world->clearWrappers(); +} + +- (void)dealloc +{ + ASSERT(allWorlds().contains(_private->world.get())); + allWorlds().remove(_private->world.get()); + + [_private release]; + _private = nil; + [super dealloc]; +} + ++ (WebScriptWorld *)standardWorld +{ + static WebScriptWorld *world = [[WebScriptWorld alloc] initWithWorld:mainThreadNormalWorld()]; + return world; +} + ++ (WebScriptWorld *)world +{ + return [[[self alloc] init] autorelease]; +} + ++ (WebScriptWorld *)scriptWorldForGlobalContext:(JSGlobalContextRef)context +{ + return [self findOrCreateWorld:currentWorld(toJS(context))]; +} + +@end + +@implementation WebScriptWorld (WebInternal) + +DOMWrapperWorld* core(WebScriptWorld *world) +{ + return world ? world->_private->world.get() : 0; +} + ++ (WebScriptWorld *)findOrCreateWorld:(DOMWrapperWorld*) world +{ + ASSERT_ARG(world, world); + + if (world == mainThreadNormalWorld()) + return [self standardWorld]; + + if (WebScriptWorld *existingWorld = allWorlds().get(world)) + return existingWorld; + + return [[[self alloc] initWithWorld:world] autorelease]; +} + +@end diff --git a/Source/WebKit/mac/WebView/WebScriptWorldInternal.h b/Source/WebKit/mac/WebView/WebScriptWorldInternal.h new file mode 100644 index 0000000..785d9c0 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebScriptWorldInternal.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "WebScriptWorld.h" + +namespace WebCore { + class DOMWrapperWorld; +} + +WebCore::DOMWrapperWorld* core(WebScriptWorld *); + +@interface WebScriptWorld (WebInternal) ++ (WebScriptWorld *)findOrCreateWorld:(WebCore::DOMWrapperWorld*)world; +@end diff --git a/Source/WebKit/mac/WebView/WebSerializedJSValue.h b/Source/WebKit/mac/WebView/WebSerializedJSValue.h new file mode 100644 index 0000000..5094776 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebSerializedJSValue.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +@class WebSerializedJSValuePrivate; + +typedef const struct OpaqueJSContext* JSContextRef; +typedef const struct OpaqueJSValue* JSValueRef; + +@interface WebSerializedJSValue : NSObject { +@private + WebSerializedJSValuePrivate *_private; +} + +- (id)initWithValue:(JSValueRef)object context:(JSContextRef)sourceContext exception:(JSValueRef*)exception; +- (JSValueRef)deserialize:(JSContextRef)destinationContext; + +@end diff --git a/Source/WebKit/mac/WebView/WebSerializedJSValue.mm b/Source/WebKit/mac/WebView/WebSerializedJSValue.mm new file mode 100644 index 0000000..d993790 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebSerializedJSValue.mm @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "WebSerializedJSValuePrivate.h" + +#import <WebCore/SerializedScriptValue.h> +#import <wtf/RefPtr.h> + +using namespace WebCore; + +@interface WebSerializedJSValuePrivate : NSObject { +@public + RefPtr<SerializedScriptValue> value; +} +@end + +@implementation WebSerializedJSValuePrivate +@end + +@implementation WebSerializedJSValue + +- (id)initWithValue:(JSValueRef)value context:(JSContextRef)sourceContext exception:(JSValueRef*)exception +{ + ASSERT_ARG(value, value); + ASSERT_ARG(sourceContext, sourceContext); + + if (!value || !sourceContext) { + [self release]; + return nil; + } + + self = [super init]; + if (!self) + return nil; + + _private = [[WebSerializedJSValuePrivate alloc] init]; + + _private->value = SerializedScriptValue::create(sourceContext, value, exception); + if (!_private->value) { + [self release]; + return nil; + } + + return self; +} + +- (id)initWithInternalRepresentation:(void *)internalRepresenatation +{ + ASSERT_ARG(internalRepresenatation, internalRepresenatation); + + if (!internalRepresenatation) { + [self release]; + return nil; + } + + self = [super init]; + if (!self) + return nil; + + _private = [[WebSerializedJSValuePrivate alloc] init]; + + _private->value = ((SerializedScriptValue*)internalRepresenatation); + if (!_private->value) { + [self release]; + return nil; + } + + return self; +} + +- (JSValueRef)deserialize:(JSContextRef)destinationContext +{ + if (!_private || !_private->value) + return 0; + return _private->value->deserialize(destinationContext, 0); +} + +- (void)dealloc +{ + [_private release]; + _private = nil; + [super dealloc]; +} + +- (void*)internalRepresentation +{ + if (!_private) + return 0; + return _private->value.get(); +} + +@end + diff --git a/Source/WebKit/mac/WebView/WebSerializedJSValuePrivate.h b/Source/WebKit/mac/WebView/WebSerializedJSValuePrivate.h new file mode 100644 index 0000000..217fe44 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebSerializedJSValuePrivate.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + * + * 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 <WebKit/WebSerializedJSValue.h> + +@interface WebSerializedJSValue(WebPrivate) +- (id)initWithInternalRepresentation:(void*)internalRepresenatation; +- (void*)internalRepresentation; +@end diff --git a/Source/WebKit/mac/WebView/WebTextCompletionController.h b/Source/WebKit/mac/WebView/WebTextCompletionController.h new file mode 100644 index 0000000..158e9b8 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebTextCompletionController.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR + * 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. + */ + +@class WebView; +@class WebHTMLView; + +@interface WebTextCompletionController : NSObject <NSTableViewDelegate, NSTableViewDataSource> { +@private + WebView *_view; + WebHTMLView *_htmlView; + NSWindow *_popupWindow; + NSTableView *_tableView; + NSArray *_completions; + NSString *_originalString; + int prefixLength; +} +- (id)initWithWebView:(WebView *)view HTMLView:(WebHTMLView *)htmlView; +- (void)doCompletion; +- (void)endRevertingChange:(BOOL)revertChange moveLeft:(BOOL)goLeft; +- (BOOL)popupWindowIsOpen; +- (BOOL)filterKeyDown:(NSEvent *)event; +- (void)_reflectSelection; +@end diff --git a/Source/WebKit/mac/WebView/WebTextCompletionController.mm b/Source/WebKit/mac/WebView/WebTextCompletionController.mm new file mode 100644 index 0000000..48b5035 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebTextCompletionController.mm @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * 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 "WebTextCompletionController.h" + +#import "DOMRangeInternal.h" +#import "WebFrameInternal.h" +#import "WebHTMLViewInternal.h" +#import "WebTypesInternal.h" +#import <WebCore/Frame.h> + +@interface NSWindow (WebNSWindowDetails) +- (void)_setForceActiveControls:(BOOL)flag; +@end + +using namespace WebCore; +using namespace std; + +// This class handles the complete: operation. +// It counts on its host view to call endRevertingChange: whenever the current completion needs to be aborted. + +// The class is in one of two modes: Popup window showing, or not. +// It is shown when a completion yields more than one match. +// If a completion yields one or zero matches, it is not shown, and there is no state carried across to the next completion. + +@implementation WebTextCompletionController + +- (id)initWithWebView:(WebView *)view HTMLView:(WebHTMLView *)htmlView +{ + self = [super init]; + if (!self) + return nil; + _view = view; + _htmlView = htmlView; + return self; +} + +- (void)dealloc +{ + [_popupWindow release]; + [_completions release]; + [_originalString release]; + + [super dealloc]; +} + +- (void)_insertMatch:(NSString *)match +{ + // FIXME: 3769654 - We should preserve case of string being inserted, even in prefix (but then also be + // able to revert that). Mimic NSText. + WebFrame *frame = [_htmlView _frame]; + NSString *newText = [match substringFromIndex:prefixLength]; + [frame _replaceSelectionWithText:newText selectReplacement:YES smartReplace:NO]; +} + +// mostly lifted from NSTextView_KeyBinding.m +- (void)_buildUI +{ + NSRect scrollFrame = NSMakeRect(0, 0, 100, 100); + NSRect tableFrame = NSZeroRect; + tableFrame.size = [NSScrollView contentSizeForFrameSize:scrollFrame.size hasHorizontalScroller:NO hasVerticalScroller:YES borderType:NSNoBorder]; + NSTableColumn *column = [[NSTableColumn alloc] init]; + [column setWidth:tableFrame.size.width]; + [column setEditable:NO]; + + _tableView = [[NSTableView alloc] initWithFrame:tableFrame]; + [_tableView setAutoresizingMask:NSViewWidthSizable]; + [_tableView addTableColumn:column]; + [column release]; + [_tableView setGridStyleMask:NSTableViewGridNone]; + [_tableView setCornerView:nil]; + [_tableView setHeaderView:nil]; + [_tableView setColumnAutoresizingStyle:NSTableViewUniformColumnAutoresizingStyle]; + [_tableView setDelegate:self]; + [_tableView setDataSource:self]; + [_tableView setTarget:self]; + [_tableView setDoubleAction:@selector(tableAction:)]; + + NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame:scrollFrame]; + [scrollView setBorderType:NSNoBorder]; + [scrollView setHasVerticalScroller:YES]; + [scrollView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [scrollView setDocumentView:_tableView]; + [_tableView release]; + + _popupWindow = [[NSWindow alloc] initWithContentRect:scrollFrame styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; + [_popupWindow setAlphaValue:0.88f]; + [_popupWindow setContentView:scrollView]; + [scrollView release]; + [_popupWindow setHasShadow:YES]; + [_popupWindow setOneShot:YES]; + [_popupWindow _setForceActiveControls:YES]; + [_popupWindow setReleasedWhenClosed:NO]; +} + +// mostly lifted from NSTextView_KeyBinding.m +- (void)_placePopupWindow:(NSPoint)topLeft +{ + int numberToShow = [_completions count]; + if (numberToShow > 20) + numberToShow = 20; + + NSRect windowFrame; + NSPoint wordStart = topLeft; + windowFrame.origin = [[_view window] convertBaseToScreen:[_htmlView convertPoint:wordStart toView:nil]]; + windowFrame.size.height = numberToShow * [_tableView rowHeight] + (numberToShow + 1) * [_tableView intercellSpacing].height; + windowFrame.origin.y -= windowFrame.size.height; + NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:[NSFont systemFontOfSize:12.0f], NSFontAttributeName, nil]; + CGFloat maxWidth = 0; + int maxIndex = -1; + int i; + for (i = 0; i < numberToShow; i++) { + float width = ceilf([[_completions objectAtIndex:i] sizeWithAttributes:attributes].width); + if (width > maxWidth) { + maxWidth = width; + maxIndex = i; + } + } + windowFrame.size.width = 100; + if (maxIndex >= 0) { + maxWidth = ceilf([NSScrollView frameSizeForContentSize:NSMakeSize(maxWidth, 100.0f) hasHorizontalScroller:NO hasVerticalScroller:YES borderType:NSNoBorder].width); + maxWidth = ceilf([NSWindow frameRectForContentRect:NSMakeRect(0.0f, 0.0f, maxWidth, 100.0f) styleMask:NSBorderlessWindowMask].size.width); + maxWidth += 5.0f; + windowFrame.size.width = max(maxWidth, windowFrame.size.width); + maxWidth = min<CGFloat>(400, windowFrame.size.width); + } + [_popupWindow setFrame:windowFrame display:NO]; + + [_tableView reloadData]; + [_tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO]; + [_tableView scrollRowToVisible:0]; + [self _reflectSelection]; + [_popupWindow setLevel:NSPopUpMenuWindowLevel]; + [_popupWindow orderFront:nil]; + [[_view window] addChildWindow:_popupWindow ordered:NSWindowAbove]; +} + +- (void)doCompletion +{ + if (!_popupWindow) { + NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker]; + if (!checker) { + LOG_ERROR("No NSSpellChecker"); + return; + } + + // Get preceeding word stem + WebFrame *frame = [_htmlView _frame]; + DOMRange *selection = kit(core(frame)->selection()->toNormalizedRange().get()); + DOMRange *wholeWord = [frame _rangeByAlteringCurrentSelection:SelectionController::AlterationExtend + direction:DirectionBackward granularity:WordGranularity]; + DOMRange *prefix = [wholeWord cloneRange]; + [prefix setEnd:[selection startContainer] offset:[selection startOffset]]; + + // Reject some NOP cases + if ([prefix collapsed]) { + NSBeep(); + return; + } + NSString *prefixStr = [frame _stringForRange:prefix]; + NSString *trimmedPrefix = [prefixStr stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + if ([trimmedPrefix length] == 0) { + NSBeep(); + return; + } + prefixLength = [prefixStr length]; + + // Lookup matches + [_completions release]; + _completions = [checker completionsForPartialWordRange:NSMakeRange(0, [prefixStr length]) inString:prefixStr language:nil inSpellDocumentWithTag:[_view spellCheckerDocumentTag]]; + [_completions retain]; + + if (!_completions || [_completions count] == 0) { + NSBeep(); + } else if ([_completions count] == 1) { + [self _insertMatch:[_completions objectAtIndex:0]]; + } else { + ASSERT(!_originalString); // this should only be set IFF we have a popup window + _originalString = [[frame _stringForRange:selection] retain]; + [self _buildUI]; + NSRect wordRect = [frame _caretRectAtNode:[wholeWord startContainer] offset:[wholeWord startOffset] affinity:NSSelectionAffinityDownstream]; + // +1 to be under the word, not the caret + // FIXME - 3769652 - Wrong positioning for right to left languages. We should line up the upper + // right corner with the caret instead of upper left, and the +1 would be a -1. + NSPoint wordLowerLeft = { NSMinX(wordRect)+1, NSMaxY(wordRect) }; + [self _placePopupWindow:wordLowerLeft]; + } + } else { + [self endRevertingChange:YES moveLeft:NO]; + } +} + +- (void)endRevertingChange:(BOOL)revertChange moveLeft:(BOOL)goLeft +{ + if (_popupWindow) { + // tear down UI + [[_view window] removeChildWindow:_popupWindow]; + [_popupWindow orderOut:self]; + // Must autorelease because event tracking code may be on the stack touching UI + [_popupWindow autorelease]; + _popupWindow = nil; + + if (revertChange) { + WebFrame *frame = [_htmlView _frame]; + [frame _replaceSelectionWithText:_originalString selectReplacement:YES smartReplace:NO]; + } else if ([_htmlView _hasSelection]) { + if (goLeft) + [_htmlView moveBackward:nil]; + else + [_htmlView moveForward:nil]; + } + [_originalString release]; + _originalString = nil; + } + // else there is no state to abort if the window was not up +} + +- (BOOL)popupWindowIsOpen +{ + return _popupWindow != nil; +} + +// WebHTMLView gives us a crack at key events it sees. Return whether we consumed the event. +// The features for the various keys mimic NSTextView. +- (BOOL)filterKeyDown:(NSEvent *)event +{ + if (!_popupWindow) + return NO; + NSString *string = [event charactersIgnoringModifiers]; + if (![string length]) + return NO; + unichar c = [string characterAtIndex:0]; + if (c == NSUpArrowFunctionKey) { + int selectedRow = [_tableView selectedRow]; + if (0 < selectedRow) { + [_tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:selectedRow - 1] byExtendingSelection:NO]; + [_tableView scrollRowToVisible:selectedRow - 1]; + } + return YES; + } + if (c == NSDownArrowFunctionKey) { + int selectedRow = [_tableView selectedRow]; + if (selectedRow < (int)[_completions count] - 1) { + [_tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:selectedRow + 1] byExtendingSelection:NO]; + [_tableView scrollRowToVisible:selectedRow + 1]; + } + return YES; + } + if (c == NSRightArrowFunctionKey || c == '\n' || c == '\r' || c == '\t') { + // FIXME: What about backtab? + [self endRevertingChange:NO moveLeft:NO]; + return YES; + } + if (c == NSLeftArrowFunctionKey) { + [self endRevertingChange:NO moveLeft:YES]; + return YES; + } + if (c == 0x1B || c == NSF5FunctionKey) { + // FIXME: F5? + [self endRevertingChange:YES moveLeft:NO]; + return YES; + } + if (c == ' ' || (c >= 0x21 && c <= 0x2F) || (c >= 0x3A && c <= 0x40) || (c >= 0x5B && c <= 0x60) || (c >= 0x7B && c <= 0x7D)) { + // FIXME: Is the above list of keys really definitive? + // Originally this code called ispunct; aren't there other punctuation keys on international keyboards? + [self endRevertingChange:NO moveLeft:NO]; + return NO; // let the char get inserted + } + return NO; +} + +- (void)_reflectSelection +{ + int selectedRow = [_tableView selectedRow]; + ASSERT(selectedRow >= 0); + ASSERT(selectedRow < (int)[_completions count]); + [self _insertMatch:[_completions objectAtIndex:selectedRow]]; +} + +- (void)tableAction:(id)sender +{ + [self _reflectSelection]; + [self endRevertingChange:NO moveLeft:NO]; +} + +- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView +{ + return [_completions count]; +} + +- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row +{ + return [_completions objectAtIndex:row]; +} + +- (void)tableViewSelectionDidChange:(NSNotification *)notification +{ + [self _reflectSelection]; +} + +@end diff --git a/Source/WebKit/mac/WebView/WebTextIterator.h b/Source/WebKit/mac/WebView/WebTextIterator.h new file mode 100644 index 0000000..d0c92f9 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebTextIterator.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR + * 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 <Foundation/Foundation.h> + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 +#define WebNSUInteger unsigned int +#else +#define WebNSUInteger NSUInteger +#endif + +@class DOMRange; +@class DOMNode; +@class WebTextIteratorPrivate; + +@interface WebTextIterator : NSObject { +@private + WebTextIteratorPrivate *_private; +} + +- (id)initWithRange:(DOMRange *)range; + +/*! + @method advance + @abstract Moves the WebTextIterator to the next bit of text or boundary between runs of text. + The iterator can break up runs of text however it finds convenient, so clients need to handle + text runs that are broken up into arbitrary pieces. + */ +- (void)advance; + +/*! + @method atEnd + @result YES if the WebTextIterator has reached the end of the DOMRange. + */ +- (BOOL)atEnd; + +/*! + @method currentTextLength + @result Length of the current text. Length of zero means that the iterator is at a boundary, + such as an image, that separates runs of text. + */ +- (WebNSUInteger)currentTextLength; + +/*! + @method currentTextPointer + @result A pointer to the current text. Like the WebTextIterator itself, the pointer becomes + invalid after any modification is made to the document; it must be used before the document + is changed or the iterator is advanced. + */ +- (const unichar *)currentTextPointer; + +/*! + @method currentRange + @abstract A function that identifies the specific document range that text corresponds to. + This can be quite costly to compute for non-text items, so when possible this should only + be called once the caller has determined that the text is text it wants to process. If you + call currentRange every time you advance the iterator, performance will be extremely slow + due to the cost of computing a DOM range. + @result A DOM range indicating the position within the document of the current text. + */ +- (DOMRange *)currentRange; + +@end + +@interface WebTextIterator (WebTextIteratorDeprecated) + +/*! + @method currentNode + @abstract A convenience method that finds the first node in currentRange; it's almost always better to use currentRange instead. + @result The current DOMNode in the WebTextIterator + */ +- (DOMNode *)currentNode; + +/*! + @method currentText + @abstract A convenience method that makes an NSString out of the current text; it's almost always better to use currentTextPointer and currentTextLength instead. + @result The current text in the WebTextIterator. + */ +- (NSString *)currentText; + +@end + +#undef WebNSUInteger diff --git a/Source/WebKit/mac/WebView/WebTextIterator.mm b/Source/WebKit/mac/WebView/WebTextIterator.mm new file mode 100644 index 0000000..6029faa --- /dev/null +++ b/Source/WebKit/mac/WebView/WebTextIterator.mm @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR + * 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 "WebTextIterator.h" + +#import "DOMNodeInternal.h" +#import "DOMRangeInternal.h" +#import "WebTypesInternal.h" +#import <JavaScriptCore/Vector.h> +#import <WebCore/TextIterator.h> +#import <WebCore/WebCoreObjCExtras.h> +#import <runtime/InitializeThreading.h> +#import <wtf/Threading.h> + +using namespace JSC; +using namespace WebCore; + +@interface WebTextIteratorPrivate : NSObject { +@public + OwnPtr<TextIterator> _textIterator; +} +@end + +@implementation WebTextIteratorPrivate + ++ (void)initialize +{ + JSC::initializeThreading(); + WTF::initializeMainThreadToProcessMainThread(); +#ifndef BUILDING_ON_TIGER + WebCoreObjCFinalizeOnMainThread(self); +#endif +} + +@end + +@implementation WebTextIterator + +- (void)dealloc +{ + [_private release]; + [super dealloc]; +} + +- (id)initWithRange:(DOMRange *)range +{ + self = [super init]; + if (!self) + return self; + + _private = [[WebTextIteratorPrivate alloc] init]; + _private->_textIterator.set(new TextIterator(core(range))); + return self; +} + +- (void)advance +{ + _private->_textIterator->advance(); +} + +- (BOOL)atEnd +{ + return _private->_textIterator->atEnd(); +} + +- (DOMRange *)currentRange +{ + return kit(_private->_textIterator->range().get()); +} + +- (const unichar *)currentTextPointer +{ + return _private->_textIterator->characters(); +} + +- (NSUInteger)currentTextLength +{ + return _private->_textIterator->length(); +} + +@end + +@implementation WebTextIterator (WebTextIteratorDeprecated) + +- (DOMNode *)currentNode +{ + return kit(_private->_textIterator->node()); +} + +- (NSString *)currentText +{ + return [NSString stringWithCharacters:_private->_textIterator->characters() length:_private->_textIterator->length()]; +} + +@end diff --git a/Source/WebKit/mac/WebView/WebUIDelegate.h b/Source/WebKit/mac/WebView/WebUIDelegate.h new file mode 100644 index 0000000..c3dd9ff --- /dev/null +++ b/Source/WebKit/mac/WebView/WebUIDelegate.h @@ -0,0 +1,575 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * + * 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 <Cocoa/Cocoa.h> +#import <Foundation/NSURLRequest.h> +#import <JavaScriptCore/WebKitAvailability.h> + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 +#define WebNSUInteger unsigned int +#else +#define WebNSUInteger NSUInteger +#endif + +/*! + @enum WebMenuItemTag + @discussion Each menu item in the default menu items array passed in + contextMenuItemsForElement:defaultMenuItems: has its tag set to one of the WebMenuItemTags. + When iterating through the default menu items array, use the tag to differentiate between them. +*/ + +enum { + WebMenuItemTagOpenLinkInNewWindow=1, + WebMenuItemTagDownloadLinkToDisk, + WebMenuItemTagCopyLinkToClipboard, + WebMenuItemTagOpenImageInNewWindow, + WebMenuItemTagDownloadImageToDisk, + WebMenuItemTagCopyImageToClipboard, + WebMenuItemTagOpenFrameInNewWindow, + WebMenuItemTagCopy, + WebMenuItemTagGoBack, + WebMenuItemTagGoForward, + WebMenuItemTagStop, + WebMenuItemTagReload, + WebMenuItemTagCut, + WebMenuItemTagPaste, + WebMenuItemTagSpellingGuess, + WebMenuItemTagNoGuessesFound, + WebMenuItemTagIgnoreSpelling, + WebMenuItemTagLearnSpelling, + WebMenuItemTagOther, + WebMenuItemTagSearchInSpotlight, + WebMenuItemTagSearchWeb, + WebMenuItemTagLookUpInDictionary, + WebMenuItemTagOpenWithDefaultApplication, + WebMenuItemPDFActualSize, + WebMenuItemPDFZoomIn, + WebMenuItemPDFZoomOut, + WebMenuItemPDFAutoSize, + WebMenuItemPDFSinglePage, + WebMenuItemPDFFacingPages, + WebMenuItemPDFContinuous, + WebMenuItemPDFNextPage, + WebMenuItemPDFPreviousPage, +}; + +/*! + @enum WebDragDestinationAction + @abstract Actions that the destination of a drag can perform. + @constant WebDragDestinationActionNone No action + @constant WebDragDestinationActionDHTML Allows DHTML (such as JavaScript) to handle the drag + @constant WebDragDestinationActionEdit Allows editable documents to be edited from the drag + @constant WebDragDestinationActionLoad Allows a location change from the drag + @constant WebDragDestinationActionAny Allows any of the above to occur +*/ +typedef enum { + WebDragDestinationActionNone = 0, + WebDragDestinationActionDHTML = 1, + WebDragDestinationActionEdit = 2, + WebDragDestinationActionLoad = 4, + WebDragDestinationActionAny = UINT_MAX +} WebDragDestinationAction; + +/*! + @enum WebDragSourceAction + @abstract Actions that the source of a drag can perform. + @constant WebDragSourceActionNone No action + @constant WebDragSourceActionDHTML Allows DHTML (such as JavaScript) to start a drag + @constant WebDragSourceActionImage Allows an image drag to occur + @constant WebDragSourceActionLink Allows a link drag to occur + @constant WebDragSourceActionSelection Allows a selection drag to occur + @constant WebDragSourceActionAny Allows any of the above to occur +*/ +typedef enum { + WebDragSourceActionNone = 0, + WebDragSourceActionDHTML = 1, + WebDragSourceActionImage = 2, + WebDragSourceActionLink = 4, + WebDragSourceActionSelection = 8, + WebDragSourceActionAny = UINT_MAX +} WebDragSourceAction; + +/*! + @protocol WebOpenPanelResultListener + @discussion This protocol is used to call back with the results of + the file open panel requested by runOpenPanelForFileButtonWithResultListener: +*/ +@protocol WebOpenPanelResultListener <NSObject> + +/*! + @method chooseFilename: + @abstract Call this method to return a filename from the file open panel. + @param fileName +*/ +- (void)chooseFilename:(NSString *)fileName; + +/*! + @method chooseFilenames: + @abstract Call this method to return an array of filenames from the file open panel. + @param fileNames +*/ +- (void)chooseFilenames:(NSArray *)fileNames WEBKIT_OBJC_METHOD_ANNOTATION(AVAILABLE_IN_WEBKIT_VERSION_4_0); + +/*! + @method cancel + @abstract Call this method to indicate that the file open panel was cancelled. +*/ +- (void)cancel; + +@end + +@class WebView; + +/*! + @category WebUIDelegate + @discussion A class that implements WebUIDelegate provides + window-related methods that may be used by Javascript, plugins and + other aspects of web pages. These methods are used to open new + windows and control aspects of existing windows. +*/ +@interface NSObject (WebUIDelegate) + +/*! + @method webView:createWebViewWithRequest: + @abstract Create a new window and begin to load the specified request. + @discussion The newly created window is hidden, and the window operations delegate on the + new WebViews will get a webViewShow: call. + @param sender The WebView sending the delegate method. + @param request The request to load. + @result The WebView for the new window. +*/ +- (WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request; + +/*! + @method webViewShow: + @param sender The WebView sending the delegate method. + @abstract Show the window that contains the top level view of the WebView, + ordering it frontmost. + @discussion This will only be called just after createWindowWithRequest: + is used to create a new window. +*/ +- (void)webViewShow:(WebView *)sender; + +/*! + @method webView:createWebViewModalDialogWithRequest: + @abstract Create a new window and begin to load the specified request. + @discussion The newly created window is hidden, and the window operations delegate on the + new WebViews will get a webViewShow: call. + @param sender The WebView sending the delegate method. + @param request The request to load. + @result The WebView for the new window. +*/ +- (WebView *)webView:(WebView *)sender createWebViewModalDialogWithRequest:(NSURLRequest *)request; + +/*! + @method webViewRunModal: + @param sender The WebView sending the delegate method. + @abstract Show the window that contains the top level view of the WebView, + ordering it frontmost. The window should be run modal in the application. + @discussion This will only be called just after createWebViewModalDialogWithRequest: + is used to create a new window. +*/ +- (void)webViewRunModal:(WebView *)sender; + +/*! + @method webViewClose: + @abstract Close the current window. + @param sender The WebView sending the delegate method. + @discussion Clients showing multiple views in one window may + choose to close only the one corresponding to this + WebView. Other clients may choose to ignore this method + entirely. +*/ +- (void)webViewClose:(WebView *)sender; + +/*! + @method webViewFocus: + @abstract Focus the current window (i.e. makeKeyAndOrderFront:). + @param The WebView sending the delegate method. + @discussion Clients showing multiple views in one window may want to + also do something to focus the one corresponding to this WebView. +*/ +- (void)webViewFocus:(WebView *)sender; + +/*! + @method webViewUnfocus: + @abstract Unfocus the current window. + @param sender The WebView sending the delegate method. + @discussion Clients showing multiple views in one window may want to + also do something to unfocus the one corresponding to this WebView. +*/ +- (void)webViewUnfocus:(WebView *)sender; + +/*! + @method webViewFirstResponder: + @abstract Get the first responder for this window. + @param sender The WebView sending the delegate method. + @discussion This method should return the focused control in the + WebView's view, if any. If the view is out of the window + hierarchy, this might return something than calling firstResponder + on the real NSWindow would. It's OK to return either nil or the + real first responder if some control not in the window has focus. +*/ +- (NSResponder *)webViewFirstResponder:(WebView *)sender; + +/*! + @method webView:makeFirstResponder: + @abstract Set the first responder for this window. + @param sender The WebView sending the delegate method. + @param responder The responder to make first (will always be a view) + @discussion responder will always be a view that is in the view + subhierarchy of the top-level web view for this WebView. If the + WebView's top level view is currently out of the view + hierarchy, it may be desirable to save the first responder + elsewhere, or possibly ignore this call. +*/ +- (void)webView:(WebView *)sender makeFirstResponder:(NSResponder *)responder; + +/*! + @method webView:setStatusText: + @abstract Set the window's status display, if any, to the specified string. + @param sender The WebView sending the delegate method. + @param text The status text to set +*/ +- (void)webView:(WebView *)sender setStatusText:(NSString *)text; + +/*! + @method webViewStatusText: + @abstract Get the currently displayed status text. + @param sender The WebView sending the delegate method. + @result The status text +*/ +- (NSString *)webViewStatusText:(WebView *)sender; + +/*! + @method webViewAreToolbarsVisible: + @abstract Determine whether the window's toolbars are currently visible + @param sender The WebView sending the delegate method. + @discussion This method should return YES if the window has any + toolbars that are currently on, besides the status bar. If the app + has more than one toolbar per window, for example a regular + command toolbar and a favorites bar, it should return YES from + this method if at least one is on. + @result YES if at least one toolbar is visible, otherwise NO. +*/ +- (BOOL)webViewAreToolbarsVisible:(WebView *)sender; + +/*! + @method webView:setToolbarsVisible: + @param sender The WebView sending the delegate method. + @abstract Set whether the window's toolbars are currently visible. + @param visible New value for toolbar visibility + @discussion Setting this to YES should turn on all toolbars + (except for a possible status bar). Setting it to NO should turn + off all toolbars (with the same exception). +*/ +- (void)webView:(WebView *)sender setToolbarsVisible:(BOOL)visible; + +/*! + @method webViewIsStatusBarVisible: + @abstract Determine whether the status bar is visible. + @param sender The WebView sending the delegate method. + @result YES if the status bar is visible, otherwise NO. +*/ +- (BOOL)webViewIsStatusBarVisible:(WebView *)sender; + +/*! + @method webView:setStatusBarVisible: + @abstract Set whether the status bar is currently visible. + @param visible The new visibility value + @discussion Setting this to YES should show the status bar, + setting it to NO should hide it. +*/ +- (void)webView:(WebView *)sender setStatusBarVisible:(BOOL)visible; + +/*! + @method webViewIsResizable: + @abstract Determine whether the window is resizable or not. + @param sender The WebView sending the delegate method. + @result YES if resizable, NO if not. + @discussion If there are multiple views in the same window, they + have have their own separate resize controls and this may need to + be handled specially. +*/ +- (BOOL)webViewIsResizable:(WebView *)sender; + +/*! + @method webView:setResizable: + @abstract Set the window to resizable or not + @param sender The WebView sending the delegate method. + @param resizable YES if the window should be made resizable, NO if not. + @discussion If there are multiple views in the same window, they + have have their own separate resize controls and this may need to + be handled specially. +*/ +- (void)webView:(WebView *)sender setResizable:(BOOL)resizable; + +/*! + @method webView:setFrame: + @abstract Set the window's frame rect + @param sender The WebView sending the delegate method. + @param frame The new window frame size + @discussion Even though a caller could set the frame directly using the NSWindow, + this method is provided so implementors of this protocol can do special + things on programmatic move/resize, like avoiding autosaving of the size. +*/ +- (void)webView:(WebView *)sender setFrame:(NSRect)frame; + +/*! + @method webViewFrame: + @param sender The WebView sending the delegate method. + @abstract REturn the window's frame rect + @discussion +*/ +- (NSRect)webViewFrame:(WebView *)sender; + +/*! + @method webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame: + @abstract Display a JavaScript alert panel. + @param sender The WebView sending the delegate method. + @param message The message to display. + @param frame The WebFrame whose JavaScript initiated this call. + @discussion Clients should visually indicate that this panel comes + from JavaScript initiated by the specified frame. The panel should have + a single OK button. +*/ +- (void)webView:(WebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame; + +/*! + @method webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame: + @abstract Display a JavaScript confirm panel. + @param sender The WebView sending the delegate method. + @param message The message to display. + @param frame The WebFrame whose JavaScript initiated this call. + @result YES if the user hit OK, NO if the user chose Cancel. + @discussion Clients should visually indicate that this panel comes + from JavaScript initiated by the specified frame. The panel should have + two buttons, e.g. "OK" and "Cancel". +*/ +- (BOOL)webView:(WebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame; + +/*! + @method webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame: + @abstract Display a JavaScript text input panel. + @param sender The WebView sending the delegate method. + @param message The message to display. + @param defaultText The initial text for the text entry area. + @param frame The WebFrame whose JavaScript initiated this call. + @result The typed text if the user hit OK, otherwise nil. + @discussion Clients should visually indicate that this panel comes + from JavaScript initiated by the specified frame. The panel should have + two buttons, e.g. "OK" and "Cancel", and an area to type text. +*/ +- (NSString *)webView:(WebView *)sender runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WebFrame *)frame; + +/*! + @method webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame: + @abstract Display a confirm panel by an "before unload" event handler. + @param sender The WebView sending the delegate method. + @param message The message to display. + @param frame The WebFrame whose JavaScript initiated this call. + @result YES if the user hit OK, NO if the user chose Cancel. + @discussion Clients should include a message in addition to the one + supplied by the web page that indicates. The panel should have + two buttons, e.g. "OK" and "Cancel". +*/ +- (BOOL)webView:(WebView *)sender runBeforeUnloadConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame; + +/*! + @method webView:runOpenPanelForFileButtonWithResultListener: + @abstract Display a file open panel for a file input control. + @param sender The WebView sending the delegate method. + @param resultListener The object to call back with the results. + @discussion This method is passed a callback object instead of giving a return + value so that it can be handled with a sheet. +*/ +- (void)webView:(WebView *)sender runOpenPanelForFileButtonWithResultListener:(id<WebOpenPanelResultListener>)resultListener; + +/*! + @method webView:runOpenPanelForFileButtonWithResultListener:allowMultipleFiles + @abstract Display a file open panel for a file input control that may allow multiple files to be selected. + @param sender The WebView sending the delegate method. + @param resultListener The object to call back with the results. + @param allowMultipleFiles YES if the open panel should allow myltiple files to be selected, NO if not. + @discussion This method is passed a callback object instead of giving a return + value so that it can be handled with a sheet. +*/ +- (void)webView:(WebView *)sender runOpenPanelForFileButtonWithResultListener:(id<WebOpenPanelResultListener>)resultListener allowMultipleFiles:(BOOL)allowMultipleFiles WEBKIT_OBJC_METHOD_ANNOTATION(AVAILABLE_IN_WEBKIT_VERSION_4_0); + +/*! + @method webView:mouseDidMoveOverElement:modifierFlags: + @abstract Update the window's feedback for mousing over links to reflect a new item the mouse is over + or new modifier flags. + @param sender The WebView sending the delegate method. + @param elementInformation Dictionary that describes the element that the mouse is over, or nil. + @param modifierFlags The modifier flags as in NSEvent. +*/ +- (void)webView:(WebView *)sender mouseDidMoveOverElement:(NSDictionary *)elementInformation modifierFlags:(WebNSUInteger)modifierFlags; + +/*! + @method webView:contextMenuItemsForElement:defaultMenuItems: + @abstract Returns the menu items to display in an element's contextual menu. + @param sender The WebView sending the delegate method. + @param element A dictionary representation of the clicked element. + @param defaultMenuItems An array of default NSMenuItems to include in all contextual menus. + @result An array of NSMenuItems to include in the contextual menu. +*/ +- (NSArray *)webView:(WebView *)sender contextMenuItemsForElement:(NSDictionary *)element defaultMenuItems:(NSArray *)defaultMenuItems; + +/*! + @method webView:validateUserInterfaceItem:defaultValidation: + @abstract Controls UI validation + @param webView The WebView sending the delegate method + @param item The user interface item being validated + @pararm defaultValidation Whether or not the WebView thinks the item is valid + @discussion This method allows the UI delegate to control WebView's validation of user interface items. + See WebView.h to see the methods to that WebView can currently validate. See NSUserInterfaceValidations and + NSValidatedUserInterfaceItem for information about UI validation. +*/ +- (BOOL)webView:(WebView *)webView validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)item defaultValidation:(BOOL)defaultValidation; + +/*! + @method webView:shouldPerformAction:fromSender: + @abstract Controls actions + @param webView The WebView sending the delegate method + @param action The action being sent + @param sender The sender of the action + @discussion This method allows the UI delegate to control WebView's behavior when an action is being sent. + For example, if the action is copy:, the delegate can return YES to allow WebView to perform its default + copy behavior or return NO and perform copy: in some other way. See WebView.h to see the actions that + WebView can perform. +*/ +- (BOOL)webView:(WebView *)webView shouldPerformAction:(SEL)action fromSender:(id)sender; + +/*! + @method webView:dragDestinationActionMaskForDraggingInfo: + @abstract Controls behavior when dragging to a WebView + @param webView The WebView sending the delegate method + @param draggingInfo The dragging info of the drag + @discussion This method is called periodically as something is dragged over a WebView. The UI delegate can return a mask + indicating which drag destination actions can occur, WebDragDestinationActionAny to allow any kind of action or + WebDragDestinationActionNone to not accept the drag. +*/ +- (WebNSUInteger)webView:(WebView *)webView dragDestinationActionMaskForDraggingInfo:(id <NSDraggingInfo>)draggingInfo; + +/*! + @method webView:willPerformDragDestinationAction:forDraggingInfo: + @abstract Informs that WebView will perform a drag destination action + @param webView The WebView sending the delegate method + @param action The drag destination action + @param draggingInfo The dragging info of the drag + @discussion This method is called after the last call to webView:dragDestinationActionMaskForDraggingInfo: after something is dropped on a WebView. + This method informs the UI delegate of the drag destination action that WebView will perform. +*/ +- (void)webView:(WebView *)webView willPerformDragDestinationAction:(WebDragDestinationAction)action forDraggingInfo:(id <NSDraggingInfo>)draggingInfo; + +/*! + @method webView:dragSourceActionMaskForPoint: + @abstract Controls behavior when dragging from a WebView + @param webView The WebView sending the delegate method + @param point The point where the drag started in the coordinates of the WebView + @discussion This method is called after the user has begun a drag from a WebView. The UI delegate can return a mask indicating + which drag source actions can occur, WebDragSourceActionAny to allow any kind of action or WebDragSourceActionNone to not begin a drag. +*/ +- (WebNSUInteger)webView:(WebView *)webView dragSourceActionMaskForPoint:(NSPoint)point; + +/*! + @method webView:willPerformDragSourceAction:fromPoint:withPasteboard: + @abstract Informs that a drag a has begun from a WebView + @param webView The WebView sending the delegate method + @param action The drag source action + @param point The point where the drag started in the coordinates of the WebView + @param pasteboard The drag pasteboard + @discussion This method is called after webView:dragSourceActionMaskForPoint: is called after the user has begun a drag from a WebView. + This method informs the UI delegate of the drag source action that will be performed and gives the delegate an opportunity to modify + the contents of the dragging pasteboard. +*/ +- (void)webView:(WebView *)webView willPerformDragSourceAction:(WebDragSourceAction)action fromPoint:(NSPoint)point withPasteboard:(NSPasteboard *)pasteboard; + +/*! + @method webView:printFrameView: + @abstract Informs that a WebFrameView needs to be printed + @param webView The WebView sending the delegate method + @param frameView The WebFrameView needing to be printed + @discussion This method is called when a script or user requests the page to be printed. + In this method the delegate can prepare the WebFrameView to be printed. Some content that WebKit + displays can be printed directly by the WebFrameView, other content will need to be handled by + the delegate. To determine if the WebFrameView can handle printing the delegate should check + WebFrameView's documentViewShouldHandlePrint, if YES then the delegate can call printDocumentView + on the WebFrameView. Otherwise the delegate will need to request a NSPrintOperation from + the WebFrameView's printOperationWithPrintInfo to handle the printing. +*/ +- (void)webView:(WebView *)sender printFrameView:(WebFrameView *)frameView; + +/*! + @method webViewHeaderHeight: + @param webView The WebView sending the delegate method + @abstract Reserve a height for the printed page header. + @result The height to reserve for the printed page header, return 0.0 to not reserve any space for a header. + @discussion The height returned will be used to calculate the rect passed to webView:drawHeaderInRect:. +*/ +- (float)webViewHeaderHeight:(WebView *)sender; + +/*! + @method webViewFooterHeight: + @param webView The WebView sending the delegate method + @abstract Reserve a height for the printed page footer. + @result The height to reserve for the printed page footer, return 0.0 to not reserve any space for a footer. + @discussion The height returned will be used to calculate the rect passed to webView:drawFooterInRect:. +*/ +- (float)webViewFooterHeight:(WebView *)sender; + +/*! + @method webView:drawHeaderInRect: + @param webView The WebView sending the delegate method + @param rect The NSRect reserved for the header of the page + @abstract The delegate should draw a header for the sender in the supplied rect. +*/ +- (void)webView:(WebView *)sender drawHeaderInRect:(NSRect)rect; + +/*! + @method webView:drawFooterInRect: + @param webView The WebView sending the delegate method + @param rect The NSRect reserved for the footer of the page + @abstract The delegate should draw a footer for the sender in the supplied rect. +*/ +- (void)webView:(WebView *)sender drawFooterInRect:(NSRect)rect; + +// The following delegate methods are deprecated in favor of the ones above that specify +// the WebFrame whose JavaScript initiated this call. +- (void)webView:(WebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message WEBKIT_OBJC_METHOD_ANNOTATION(AVAILABLE_WEBKIT_VERSION_1_0_AND_LATER_BUT_DEPRECATED_IN_WEBKIT_VERSION_3_0); +- (BOOL)webView:(WebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message WEBKIT_OBJC_METHOD_ANNOTATION(AVAILABLE_WEBKIT_VERSION_1_0_AND_LATER_BUT_DEPRECATED_IN_WEBKIT_VERSION_3_0); +- (NSString *)webView:(WebView *)sender runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText WEBKIT_OBJC_METHOD_ANNOTATION(AVAILABLE_WEBKIT_VERSION_1_0_AND_LATER_BUT_DEPRECATED_IN_WEBKIT_VERSION_3_0); + +// The following delegate methods are deprecated. Content rect calculations are now done automatically. +- (void)webView:(WebView *)sender setContentRect:(NSRect)frame WEBKIT_OBJC_METHOD_ANNOTATION(AVAILABLE_WEBKIT_VERSION_1_0_AND_LATER_BUT_DEPRECATED_IN_WEBKIT_VERSION_3_0); +- (NSRect)webViewContentRect:(WebView *)sender WEBKIT_OBJC_METHOD_ANNOTATION(AVAILABLE_WEBKIT_VERSION_1_0_AND_LATER_BUT_DEPRECATED_IN_WEBKIT_VERSION_3_0); + +@end + +#undef WebNSUInteger diff --git a/Source/WebKit/mac/WebView/WebUIDelegatePrivate.h b/Source/WebKit/mac/WebView/WebUIDelegatePrivate.h new file mode 100644 index 0000000..0b62bcb --- /dev/null +++ b/Source/WebKit/mac/WebView/WebUIDelegatePrivate.h @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * 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 <WebKit/WebUIDelegate.h> + +#if !defined(ENABLE_DASHBOARD_SUPPORT) +#define ENABLE_DASHBOARD_SUPPORT 1 +#endif + +#if !defined(ENABLE_FULLSCREEN_API) +#define ENABLE_FULLSCREEN_API 1 +#endif + +// Mail on Tiger expects the old value for WebMenuItemTagSearchInGoogle +#define WebMenuItemTagSearchInGoogle OldWebMenuItemTagSearchWeb + +#define WEBMENUITEMTAG_WEBKIT_3_0_SPI_START 2000 +enum { + // The next three values were used in WebKit 2.0 for SPI. In WebKit 3.0 these are API, with different values. + OldWebMenuItemTagSearchInSpotlight = 1000, + OldWebMenuItemTagSearchWeb, + OldWebMenuItemTagLookUpInDictionary, + // FIXME: These should move to WebUIDelegate.h as part of the WebMenuItemTag enum there, when we're not in API freeze + // Note that these values must be kept aligned with values in WebCore/ContextMenuItem.h + WebMenuItemTagOpenLink = WEBMENUITEMTAG_WEBKIT_3_0_SPI_START, + WebMenuItemTagIgnoreGrammar, + WebMenuItemTagSpellingMenu, + WebMenuItemTagShowSpellingPanel, + WebMenuItemTagCheckSpelling, + WebMenuItemTagCheckSpellingWhileTyping, + WebMenuItemTagCheckGrammarWithSpelling, + WebMenuItemTagFontMenu, + WebMenuItemTagShowFonts, + WebMenuItemTagBold, + WebMenuItemTagItalic, + WebMenuItemTagUnderline, + WebMenuItemTagOutline, + WebMenuItemTagStyles, + WebMenuItemTagShowColors, + WebMenuItemTagSpeechMenu, + WebMenuItemTagStartSpeaking, + WebMenuItemTagStopSpeaking, + WebMenuItemTagWritingDirectionMenu, + WebMenuItemTagDefaultDirection, + WebMenuItemTagLeftToRight, + WebMenuItemTagRightToLeft, + WebMenuItemPDFSinglePageScrolling, + WebMenuItemPDFFacingPagesScrolling, + WebMenuItemTagInspectElement, + WebMenuItemTagTextDirectionMenu, + WebMenuItemTagTextDirectionDefault, + WebMenuItemTagTextDirectionLeftToRight, + WebMenuItemTagTextDirectionRightToLeft, + WebMenuItemTagCorrectSpellingAutomatically, + WebMenuItemTagSubstitutionsMenu, + WebMenuItemTagShowSubstitutions, + WebMenuItemTagSmartCopyPaste, + WebMenuItemTagSmartQuotes, + WebMenuItemTagSmartDashes, + WebMenuItemTagSmartLinks, + WebMenuItemTagTextReplacement, + WebMenuItemTagTransformationsMenu, + WebMenuItemTagMakeUpperCase, + WebMenuItemTagMakeLowerCase, + WebMenuItemTagCapitalize, + WebMenuItemTagChangeBack, + WebMenuItemTagBaseApplication = 10000 +}; + +@class WebSecurityOrigin; + +@protocol WebGeolocationPolicyListener <NSObject> +- (void)allow; +- (void)deny; +@end + +#if ENABLE_FULLSCREEN_API +@protocol WebKitFullScreenListener<NSObject> +- (void)webkitWillEnterFullScreen; +- (void)webkitDidEnterFullScreen; +- (void)webkitWillExitFullScreen; +- (void)webkitDidExitFullScreen; +@end +#endif + +@interface NSObject (WebUIDelegatePrivate) + +- (void)webView:(WebView *)webView addMessageToConsole:(NSDictionary *)message; + +- (NSView *)webView:(WebView *)webView plugInViewWithArguments:(NSDictionary *)arguments; + +#if ENABLE_DASHBOARD_SUPPORT +// regions is an dictionary whose keys are regions label and values are arrays of WebDashboardRegions. +- (void)webView:(WebView *)webView dashboardRegionsChanged:(NSDictionary *)regions; +#endif + +- (void)webView:(WebView *)sender dragImage:(NSImage *)anImage at:(NSPoint)viewLocation offset:(NSSize)initialOffset event:(NSEvent *)event pasteboard:(NSPasteboard *)pboard source:(id)sourceObj slideBack:(BOOL)slideFlag forView:(NSView *)view; +- (void)webView:(WebView *)sender didDrawRect:(NSRect)rect; +- (void)webView:(WebView *)sender didScrollDocumentInFrameView:(WebFrameView *)frameView; +// FIXME: If we ever make this method public, it should include a WebFrame parameter. +- (BOOL)webViewShouldInterruptJavaScript:(WebView *)sender; +- (void)webView:(WebView *)sender willPopupMenu:(NSMenu *)menu; +- (void)webView:(WebView *)sender contextMenuItemSelected:(NSMenuItem *)item forElement:(NSDictionary *)element; +- (void)webView:(WebView *)sender saveFrameView:(WebFrameView *)frameView showingPanel:(BOOL)showingPanel; +- (BOOL)webView:(WebView *)sender shouldHaltPlugin:(DOMNode *)pluginNode isWindowed:(BOOL)isWindowed pluginName:(NSString *)pluginName; +- (BOOL)webView:(WebView *)sender didPressMissingPluginButton:(DOMElement *)element; +/*! + @method webView:frame:exceededDatabaseQuotaForSecurityOrigin:database: + @param sender The WebView sending the delegate method. + @param frame The WebFrame whose JavaScript initiated this call. + @param origin The security origin that needs a larger quota. + @param databaseIdentifier The identifier of the database involved. +*/ +- (void)webView:(WebView *)sender frame:(WebFrame *)frame exceededDatabaseQuotaForSecurityOrigin:(WebSecurityOrigin *)origin database:(NSString *)databaseIdentifier; + +/*! + @method webView:exceededApplicationCacheOriginQuotaForSecurityOrigin: + @param sender The WebView sending the delegate method. + @param origin The security origin that needs a larger quota + @discussion This method is called when a page attempts to store more in the Application Cache + for an origin than was allowed by the quota (or default) set for the origin. This allows the + quota to be increased for the security origin. +*/ +- (void)webView:(WebView *)sender exceededApplicationCacheOriginQuotaForSecurityOrigin:(WebSecurityOrigin *)origin; + +- (WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request windowFeatures:(NSDictionary *)features; + +- (BOOL)webView:(WebView *)sender shouldReplaceUploadFile:(NSString *)path usingGeneratedFilename:(NSString **)filename; +- (NSString *)webView:(WebView *)sender generateReplacementFile:(NSString *)path; + +/*! + @method webView:decidePolicyForGeolocationRequestFromOrigin:frame:listener: + @abstract + @param webView The WebView sending the delegate method. + @param origin The security origin that would like to use Geolocation. + @param frame The WebFrame whose JavaScript initiated this call. + @param listener The object to call when the decision is made +*/ +- (void)webView:(WebView *)webView decidePolicyForGeolocationRequestFromOrigin:(WebSecurityOrigin *)origin + frame:(WebFrame *)frame + listener:(id<WebGeolocationPolicyListener>)listener; + +- (void)webView:(WebView *)sender formDidFocusNode:(DOMNode *)node; +- (void)webView:(WebView *)sender formDidBlurNode:(DOMNode *)node; + +/*! + @method webView:printFrame: + @abstract Informs that a WebFrame needs to be printed + @param webView The WebView sending the delegate method + @param frameView The WebFrame needing to be printed + @discussion This method is called when a script or user requests the page to be printed. +*/ +- (void)webView:(WebView *)sender printFrame:(WebFrame *)frame; + +#if ENABLE_FULLSCREEN_API +- (BOOL)webView:(WebView *)sender supportsFullScreenForElement:(DOMElement *)element; +- (void)webView:(WebView *)sender enterFullScreenForElement:(DOMElement *)element; +- (void)webView:(WebView *)sender exitFullScreenForElement:(DOMElement *)element; +#endif + +- (void)webView:(WebView *)sender didDrawFrame:(WebFrame *)frame; + +@end diff --git a/Source/WebKit/mac/WebView/WebVideoFullscreenController.h b/Source/WebKit/mac/WebView/WebVideoFullscreenController.h new file mode 100644 index 0000000..69ba9f4 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebVideoFullscreenController.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#if ENABLE(VIDEO) + +#import <wtf/RefPtr.h> + +namespace WebCore { + class HTMLMediaElement; +} + +@protocol WebVideoFullscreenControllerDelegate; +@class WebVideoFullscreenHUDWindowController; +@class WebWindowFadeAnimation; + +@interface WebVideoFullscreenController : NSWindowController { +@private + RefPtr<WebCore::HTMLMediaElement> _mediaElement; // (retain) + id <WebVideoFullscreenControllerDelegate> _delegate; // (assign) + + NSWindow *_backgroundFullscreenWindow; // (retain) + WebVideoFullscreenHUDWindowController *_hudController; // (retain) + + WebWindowFadeAnimation *_fadeAnimation; // (retain) + + BOOL _isEndingFullscreen; + BOOL _isWindowLoaded; + BOOL _forceDisableAnimation; + uint32_t _idleDisplaySleepAssertion; + uint32_t _idleSystemSleepAssertion; + NSTimer *_tickleTimer; + SystemUIMode _savedUIMode; + SystemUIOptions _savedUIOptions; +} + +- (id <WebVideoFullscreenControllerDelegate>)delegate; +- (void)setDelegate:(id <WebVideoFullscreenControllerDelegate>)delegate; + +- (void)setMediaElement:(WebCore::HTMLMediaElement*)mediaElement; +- (WebCore::HTMLMediaElement*)mediaElement; + +- (void)enterFullscreen:(NSScreen *)screen; +- (void)exitFullscreen; + +@end + +#endif // ENABLE(VIDEO) diff --git a/Source/WebKit/mac/WebView/WebVideoFullscreenController.mm b/Source/WebKit/mac/WebView/WebVideoFullscreenController.mm new file mode 100644 index 0000000..93e3d8c --- /dev/null +++ b/Source/WebKit/mac/WebView/WebVideoFullscreenController.mm @@ -0,0 +1,603 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#if ENABLE(VIDEO) + +#import "WebVideoFullscreenController.h" + +#import "WebTypesInternal.h" +#import "WebVideoFullscreenHUDWindowController.h" +#import "WebWindowAnimation.h" +#import <IOKit/pwr_mgt/IOPMLib.h> +#import <OSServices/Power.h> +#import <QTKit/QTKit.h> +#import <WebCore/HTMLMediaElement.h> +#import <WebCore/SoftLinking.h> +#import <objc/objc-runtime.h> +#import <wtf/UnusedParam.h> + +SOFT_LINK_FRAMEWORK(QTKit) +SOFT_LINK_CLASS(QTKit, QTMovieLayer) + +SOFT_LINK_POINTER(QTKit, QTMovieRateDidChangeNotification, NSString *) + +#define QTMovieRateDidChangeNotification getQTMovieRateDidChangeNotification() +static const NSTimeInterval tickleTimerInterval = 1.0; + +@interface WebVideoFullscreenWindow : NSWindow +#if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_TIGER) +<NSAnimationDelegate> +#endif +{ + SEL _controllerActionOnAnimationEnd; + WebWindowScaleAnimation *_fullscreenAnimation; // (retain) +} +- (void)animateFromRect:(NSRect)startRect toRect:(NSRect)endRect withSubAnimation:(NSAnimation *)subAnimation controllerAction:(SEL)controllerAction; +@end + +@interface WebVideoFullscreenController(HUDWindowControllerDelegate) <WebVideoFullscreenHUDWindowControllerDelegate> +- (void)requestExitFullscreenWithAnimation:(BOOL)animation; +- (void)updateMenuAndDockForFullscreen; +- (void)updatePowerAssertions; +@end + +@interface NSWindow(IsOnActiveSpaceAdditionForTigerAndLeopard) +- (BOOL)isOnActiveSpace; +@end + +@implementation WebVideoFullscreenController +- (id)init +{ + // Do not defer window creation, to make sure -windowNumber is created (needed by WebWindowScaleAnimation). + NSWindow *window = [[WebVideoFullscreenWindow alloc] initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; + self = [super initWithWindow:window]; + [window release]; + if (!self) + return nil; + [self windowDidLoad]; + return self; + +} +- (void)dealloc +{ + ASSERT(!_backgroundFullscreenWindow); + ASSERT(!_fadeAnimation); + [_tickleTimer invalidate]; + [_tickleTimer release]; + _tickleTimer = nil; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; +} + +- (WebVideoFullscreenWindow *)fullscreenWindow +{ + return (WebVideoFullscreenWindow *)[super window]; +} + +- (void)windowDidLoad +{ +#ifdef BUILDING_ON_TIGER + // WebVideoFullscreenController is not supported on Tiger: + ASSERT_NOT_REACHED(); +#else + WebVideoFullscreenWindow *window = [self fullscreenWindow]; + QTMovieLayer *layer = [[getQTMovieLayerClass() alloc] init]; + [[window contentView] setLayer:layer]; + [[window contentView] setWantsLayer:YES]; + if (_mediaElement && _mediaElement->platformMedia().type == WebCore::PlatformMedia::QTMovieType) + [layer setMovie:_mediaElement->platformMedia().media.qtMovie]; + [window setHasShadow:YES]; // This is nicer with a shadow. + [window setLevel:NSPopUpMenuWindowLevel-1]; + [layer release]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidResignActive:) name:NSApplicationDidResignActiveNotification object:NSApp]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidChangeScreenParameters:) name:NSApplicationDidChangeScreenParametersNotification object:NSApp]; +#endif +} + +- (WebCore::HTMLMediaElement*)mediaElement +{ + return _mediaElement.get(); +} + +- (void)setMediaElement:(WebCore::HTMLMediaElement*)mediaElement +{ +#ifdef BUILDING_ON_TIGER + // WebVideoFullscreenController is not supported on Tiger: + ASSERT_NOT_REACHED(); +#else + _mediaElement = mediaElement; + if ([self isWindowLoaded]) { + QTMovie *movie = _mediaElement->platformMedia().type == WebCore::PlatformMedia::QTMovieType ? _mediaElement->platformMedia().media.qtMovie : 0; + QTMovieLayer *movieLayer = (QTMovieLayer *)[[[self fullscreenWindow] contentView] layer]; + + ASSERT(movieLayer && [movieLayer isKindOfClass:[getQTMovieLayerClass() class]]); + ASSERT(movie); + [movieLayer setMovie:movie]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(rateChanged:) + name:QTMovieRateDidChangeNotification + object:movie]; + } +#endif +} + +- (id <WebVideoFullscreenControllerDelegate>)delegate +{ + return _delegate; +} + +- (void)setDelegate:(id <WebVideoFullscreenControllerDelegate>)delegate +{ + _delegate = delegate; +} + +- (CGFloat)clearFadeAnimation +{ + [_fadeAnimation stopAnimation]; + CGFloat previousAlpha = [_fadeAnimation currentAlpha]; + [_fadeAnimation setWindow:nil]; + [_fadeAnimation release]; + _fadeAnimation = nil; + return previousAlpha; +} + +- (void)windowDidExitFullscreen +{ + [self clearFadeAnimation]; + [[self window] close]; + [self setWindow:nil]; + [self updateMenuAndDockForFullscreen]; + [self updatePowerAssertions]; + [_hudController setDelegate:nil]; + [_hudController release]; + _hudController = nil; + [_backgroundFullscreenWindow close]; + [_backgroundFullscreenWindow release]; + _backgroundFullscreenWindow = nil; + + [self autorelease]; // Associated -retain is in -exitFullscreen. + _isEndingFullscreen = NO; +} + +- (void)windowDidEnterFullscreen +{ + [self clearFadeAnimation]; + + ASSERT(!_hudController); + _hudController = [[WebVideoFullscreenHUDWindowController alloc] init]; + [_hudController setDelegate:self]; + + [self updateMenuAndDockForFullscreen]; + [self updatePowerAssertions]; + [NSCursor setHiddenUntilMouseMoves:YES]; + + // Give the HUD keyboard focus initially + [_hudController fadeWindowIn]; +} + +- (NSRect)mediaElementRect +{ + return _mediaElement->screenRect(); +} + +- (void)applicationDidResignActive:(NSNotification*)notification +{ + // Check to see if the fullscreenWindow is on the active space; this function is available + // on 10.6 and later, so default to YES if the function is not available: + NSWindow* fullscreenWindow = [self fullscreenWindow]; + BOOL isOnActiveSpace = ([fullscreenWindow respondsToSelector:@selector(isOnActiveSpace)] ? [fullscreenWindow isOnActiveSpace] : YES); + + // Replicate the QuickTime Player (X) behavior when losing active application status: + // Is the fullscreen screen the main screen? (Note: this covers the case where only a + // single screen is available.) Is the fullscreen screen on the current space? IFF so, + // then exit fullscreen mode. + if ([fullscreenWindow screen] == [[NSScreen screens] objectAtIndex:0] && isOnActiveSpace) + [self requestExitFullscreenWithAnimation:NO]; +} + + +// MARK: - +// MARK: Exposed Interface + +static void constrainFrameToRatioOfFrame(NSRect *frameToConstrain, const NSRect *frame) +{ + // Keep a constrained aspect ratio for the destination window + double originalRatio = frame->size.width / frame->size.height; + double newRatio = frameToConstrain->size.width / frameToConstrain->size.height; + if (newRatio > originalRatio) { + double newWidth = originalRatio * frameToConstrain->size.height; + double diff = frameToConstrain->size.width - newWidth; + frameToConstrain->size.width = newWidth; + frameToConstrain->origin.x += diff / 2; + } else { + double newHeight = frameToConstrain->size.width / originalRatio; + double diff = frameToConstrain->size.height - newHeight; + frameToConstrain->size.height = newHeight; + frameToConstrain->origin.y += diff / 2; + } +} + +static NSWindow *createBackgroundFullscreenWindow(NSRect frame, int level) +{ + NSWindow *window = [[NSWindow alloc] initWithContentRect:frame styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; + [window setOpaque:YES]; + [window setBackgroundColor:[NSColor blackColor]]; + [window setLevel:level]; + [window setReleasedWhenClosed:NO]; + return window; +} + +- (void)setupFadeAnimationIfNeededAndFadeIn:(BOOL)fadeIn +{ + CGFloat initialAlpha = fadeIn ? 0 : 1; + if (_fadeAnimation) { + // Make sure we support queuing animation if the previous one isn't over yet + initialAlpha = [self clearFadeAnimation]; + } + if (!_forceDisableAnimation) + _fadeAnimation = [[WebWindowFadeAnimation alloc] initWithDuration:0.2 window:_backgroundFullscreenWindow initialAlpha:initialAlpha finalAlpha:fadeIn ? 1 : 0]; +} + +- (void)enterFullscreen:(NSScreen *)screen +{ + if (!screen) + screen = [NSScreen mainScreen]; + + NSRect frame = [self mediaElementRect]; + NSRect endFrame = [screen frame]; + constrainFrameToRatioOfFrame(&endFrame, &frame); + + // Create a black window if needed + if (!_backgroundFullscreenWindow) + _backgroundFullscreenWindow = createBackgroundFullscreenWindow([screen frame], [[self window] level]-1); + else + [_backgroundFullscreenWindow setFrame:[screen frame] display:NO]; + + [self setupFadeAnimationIfNeededAndFadeIn:YES]; + if (_forceDisableAnimation) { + // This will disable scale animation + frame = NSZeroRect; + } + [[self fullscreenWindow] animateFromRect:frame toRect:endFrame withSubAnimation:_fadeAnimation controllerAction:@selector(windowDidEnterFullscreen)]; + + [_backgroundFullscreenWindow orderWindow:NSWindowBelow relativeTo:[[self fullscreenWindow] windowNumber]]; +} + +- (void)exitFullscreen +{ + if (_isEndingFullscreen) + return; + _isEndingFullscreen = YES; + [_hudController closeWindow]; + + NSRect endFrame = [self mediaElementRect]; + + [self setupFadeAnimationIfNeededAndFadeIn:NO]; + if (_forceDisableAnimation) { + // This will disable scale animation + endFrame = NSZeroRect; + } + + // We have to retain ourselves because we want to be alive for the end of the animation. + // If our owner releases us we could crash if this is not the case. + // Balanced in windowDidExitFullscreen + [self retain]; + + [[self fullscreenWindow] animateFromRect:[[self window] frame] toRect:endFrame withSubAnimation:_fadeAnimation controllerAction:@selector(windowDidExitFullscreen)]; +} + +- (void)applicationDidChangeScreenParameters:(NSNotification*)notification +{ + // The user may have changed the main screen by moving the menu bar, or they may have changed + // the Dock's size or location, or they may have changed the fullscreen screen's dimensions. + // Update our presentation parameters, and ensure that the full screen window occupies the + // entire screen: + [self updateMenuAndDockForFullscreen]; + [[self window] setFrame:[[[self window] screen] frame] display:YES]; +} + +- (void)updateMenuAndDockForFullscreen +{ + // NSApplicationPresentationOptions is available on > 10.6 only: +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + NSApplicationPresentationOptions options = NSApplicationPresentationDefault; + NSScreen* fullscreenScreen = [[self window] screen]; + + if (!_isEndingFullscreen) { + // Auto-hide the menu bar if the fullscreenScreen contains the menu bar: + // NOTE: if the fullscreenScreen contains the menu bar but not the dock, we must still + // auto-hide the dock, or an exception will be thrown. + if ([[NSScreen screens] objectAtIndex:0] == fullscreenScreen) + options |= (NSApplicationPresentationAutoHideMenuBar | NSApplicationPresentationAutoHideDock); + // Check if the current screen contains the dock by comparing the screen's frame to its + // visibleFrame; if a dock is present, the visibleFrame will differ. If the current screen + // contains the dock, hide it. + else if (!NSEqualRects([fullscreenScreen frame], [fullscreenScreen visibleFrame])) + options |= NSApplicationPresentationAutoHideDock; + } + + if ([NSApp respondsToSelector:@selector(setPresentationOptions:)]) + [NSApp setPresentationOptions:options]; + else +#endif + SetSystemUIMode(_isEndingFullscreen ? kUIModeNormal : kUIModeAllHidden, 0); +} + +#if !defined(BUILDING_ON_TIGER) // IOPMAssertionCreateWithName not defined on < 10.5 +- (void)_disableIdleDisplaySleep +{ + if (_idleDisplaySleepAssertion == kIOPMNullAssertionID) +#if defined(BUILDING_ON_LEOPARD) // IOPMAssertionCreateWithName is not defined in the 10.5 SDK + IOPMAssertionCreate(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, &_idleDisplaySleepAssertion); +#else // IOPMAssertionCreate is depreciated in > 10.5 + IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, CFSTR("WebKit playing a video fullscreen."), &_idleDisplaySleepAssertion); +#endif +} + +- (void)_enableIdleDisplaySleep +{ + if (_idleDisplaySleepAssertion != kIOPMNullAssertionID) { + IOPMAssertionRelease(_idleDisplaySleepAssertion); + _idleDisplaySleepAssertion = kIOPMNullAssertionID; + } +} + +- (void)_disableIdleSystemSleep +{ + if (_idleSystemSleepAssertion == kIOPMNullAssertionID) +#if defined(BUILDING_ON_LEOPARD) // IOPMAssertionCreateWithName is not defined in the 10.5 SDK + IOPMAssertionCreate(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, &_idleSystemSleepAssertion); +#else // IOPMAssertionCreate is depreciated in > 10.5 + IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, CFSTR("WebKit playing a video fullscreen."), &_idleSystemSleepAssertion); +#endif +} + +- (void)_enableIdleSystemSleep +{ + if (_idleSystemSleepAssertion != kIOPMNullAssertionID) { + IOPMAssertionRelease(_idleSystemSleepAssertion); + _idleSystemSleepAssertion = kIOPMNullAssertionID; + } +} + +- (void)_enableTickleTimer +{ + [_tickleTimer invalidate]; + [_tickleTimer release]; + _tickleTimer = [[NSTimer scheduledTimerWithTimeInterval:tickleTimerInterval target:self selector:@selector(_tickleTimerFired) userInfo:nil repeats:YES] retain]; +} + +- (void)_disableTickleTimer +{ + [_tickleTimer invalidate]; + [_tickleTimer release]; + _tickleTimer = nil; +} + +- (void)_tickleTimerFired +{ + UpdateSystemActivity(OverallAct); +} +#endif + +- (void)updatePowerAssertions +{ +#if !defined(BUILDING_ON_TIGER) + float rate = 0; + if (_mediaElement && _mediaElement->platformMedia().type == WebCore::PlatformMedia::QTMovieType) + rate = [_mediaElement->platformMedia().media.qtMovie rate]; + + if (rate && !_isEndingFullscreen) { + [self _disableIdleSystemSleep]; + [self _disableIdleDisplaySleep]; + [self _enableTickleTimer]; + } else { + [self _enableIdleSystemSleep]; + [self _enableIdleDisplaySleep]; + [self _disableTickleTimer]; + } +#endif +} + +// MARK: - +// MARK: Window callback + +- (void)_requestExit +{ + if (_mediaElement) + _mediaElement->exitFullscreen(); + _forceDisableAnimation = NO; +} + +- (void)requestExitFullscreenWithAnimation:(BOOL)animation +{ + if (_isEndingFullscreen) + return; + + _forceDisableAnimation = !animation; + [self performSelector:@selector(_requestExit) withObject:nil afterDelay:0]; + +} + +- (void)requestExitFullscreen +{ + [self requestExitFullscreenWithAnimation:YES]; +} + +- (void)fadeHUDIn +{ + [_hudController fadeWindowIn]; +} + +// MARK: - +// MARK: QTMovie callbacks + +- (void)rateChanged:(NSNotification *)unusedNotification +{ + UNUSED_PARAM(unusedNotification); + [_hudController updateRate]; + [self updatePowerAssertions]; +} + +@end + +@implementation WebVideoFullscreenWindow + +- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag +{ + UNUSED_PARAM(aStyle); + self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:bufferingType defer:flag]; + if (!self) + return nil; + [self setOpaque:NO]; + [self setBackgroundColor:[NSColor clearColor]]; + [self setIgnoresMouseEvents:NO]; + [self setAcceptsMouseMovedEvents:YES]; + return self; +} + +- (void)dealloc +{ + ASSERT(!_fullscreenAnimation); + [super dealloc]; +} + +- (BOOL)resignFirstResponder +{ + return NO; +} + +- (BOOL)canBecomeKeyWindow +{ + return NO; +} + +- (void)mouseDown:(NSEvent *)theEvent +{ + UNUSED_PARAM(theEvent); +} + +- (void)cancelOperation:(id)sender +{ + UNUSED_PARAM(sender); + [[self windowController] requestExitFullscreen]; +} + +- (void)animatedResizeDidEnd +{ + // Call our windowController. + if (_controllerActionOnAnimationEnd) + [[self windowController] performSelector:_controllerActionOnAnimationEnd]; + _controllerActionOnAnimationEnd = NULL; +} + +// +// This function will animate a change of frame rectangle +// We support queuing animation, that means that we'll correctly +// interrupt the running animation, and queue the next one. +// +- (void)animateFromRect:(NSRect)startRect toRect:(NSRect)endRect withSubAnimation:(NSAnimation *)subAnimation controllerAction:(SEL)controllerAction +{ + _controllerActionOnAnimationEnd = controllerAction; + + BOOL wasAnimating = NO; + if (_fullscreenAnimation) { + wasAnimating = YES; + + // Interrupt any running animation. + [_fullscreenAnimation stopAnimation]; + + // Save the current rect to ensure a smooth transition. + startRect = [_fullscreenAnimation currentFrame]; + [_fullscreenAnimation release]; + _fullscreenAnimation = nil; + } + + if (NSIsEmptyRect(startRect) || NSIsEmptyRect(endRect)) { + // Fakely end the subanimation. + [subAnimation setCurrentProgress:1.0]; + // And remove the weak link to the window. + [subAnimation stopAnimation]; + + [self setFrame:endRect display:NO]; + [self makeKeyAndOrderFront:self]; + [self animatedResizeDidEnd]; + return; + } + + if (!wasAnimating) { + // We'll downscale the window during the animation based on the higher resolution rect + BOOL higherResolutionIsEndRect = startRect.size.width < endRect.size.width && startRect.size.height < endRect.size.height; + [self setFrame:higherResolutionIsEndRect ? endRect : startRect display:NO]; + } + + ASSERT(!_fullscreenAnimation); + _fullscreenAnimation = [[WebWindowScaleAnimation alloc] initWithHintedDuration:0.2 window:self initalFrame:startRect finalFrame:endRect]; + [_fullscreenAnimation setSubAnimation:subAnimation]; + [_fullscreenAnimation setDelegate:self]; + + // Make sure the animation has scaled the window before showing it. + [_fullscreenAnimation setCurrentProgress:0]; + [self makeKeyAndOrderFront:self]; + + [_fullscreenAnimation startAnimation]; +} + +- (void)animationDidEnd:(NSAnimation *)animation +{ +#if !defined(BUILDING_ON_TIGER) // Animations are never threaded on Tiger. + if (![NSThread isMainThread]) { + [self performSelectorOnMainThread:@selector(animationDidEnd:) withObject:animation waitUntilDone:NO]; + return; + } +#endif + if (animation != _fullscreenAnimation) + return; + + // The animation is not really over and was interrupted + // Don't send completion events. + if ([animation currentProgress] < 1.0) + return; + + // Ensure that animation (and subanimation) don't keep + // the weak reference to the window ivar that may be destroyed from + // now on. + [_fullscreenAnimation setWindow:nil]; + + [_fullscreenAnimation autorelease]; + _fullscreenAnimation = nil; + + [self animatedResizeDidEnd]; +} + +- (void)mouseMoved:(NSEvent *)theEvent +{ + [[self windowController] fadeHUDIn]; +} + +@end + +#endif /* ENABLE(VIDEO) */ diff --git a/Source/WebKit/mac/WebView/WebVideoFullscreenHUDWindowController.h b/Source/WebKit/mac/WebView/WebVideoFullscreenHUDWindowController.h new file mode 100644 index 0000000..a3d74d2 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebVideoFullscreenHUDWindowController.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#if ENABLE(VIDEO) + +namespace WebCore { + class HTMLMediaElement; +} + +@protocol WebVideoFullscreenHUDWindowControllerDelegate; + +@interface WebVideoFullscreenHUDWindowController : NSWindowController +{ + id <WebVideoFullscreenHUDWindowControllerDelegate> _delegate; + NSTimer *_timelineUpdateTimer; +#if !defined(BUILDING_ON_TIGER) + NSTrackingArea *_area; +#endif + BOOL _mouseIsInHUD; + BOOL _isEndingFullscreen; + BOOL _isScrubbing; + + NSControl *_timeline; + NSTextField *_remainingTimeText; + NSTextField *_elapsedTimeText; + NSControl *_volumeSlider; + NSButton *_playButton; +} + +- (id <WebVideoFullscreenHUDWindowControllerDelegate>)delegate; +- (void)setDelegate:(id <WebVideoFullscreenHUDWindowControllerDelegate>)delegate; +- (void)fadeWindowIn; +- (void)fadeWindowOut; +- (void)closeWindow; +- (void)updateRate; + +@end + +@protocol WebVideoFullscreenHUDWindowControllerDelegate <NSObject> +- (void)requestExitFullscreen; +- (WebCore::HTMLMediaElement*)mediaElement; +@end + +#endif diff --git a/Source/WebKit/mac/WebView/WebVideoFullscreenHUDWindowController.mm b/Source/WebKit/mac/WebView/WebVideoFullscreenHUDWindowController.mm new file mode 100644 index 0000000..0a5aca4 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebVideoFullscreenHUDWindowController.mm @@ -0,0 +1,676 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. + */ + +#if ENABLE(VIDEO) + +#import "WebVideoFullscreenHUDWindowController.h" + +#import "WebKitSystemInterface.h" +#import "WebTypesInternal.h" +#import <JavaScriptCore/RetainPtr.h> +#import <JavaScriptCore/UnusedParam.h> +#import <WebCore/HTMLMediaElement.h> + +using namespace WebCore; +using namespace std; + +static inline CGFloat webkit_CGFloor(CGFloat value) +{ + if (sizeof(value) == sizeof(float)) + return floorf(value); + return floor(value); +} + +#define HAVE_MEDIA_CONTROL (!defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)) + +@interface WebVideoFullscreenHUDWindowController (Private) <NSWindowDelegate> + +- (void)updateTime; +- (void)timelinePositionChanged:(id)sender; +- (float)currentTime; +- (void)setCurrentTime:(float)currentTime; +- (double)duration; + +- (void)volumeChanged:(id)sender; +- (double)maxVolume; +- (double)volume; +- (void)setVolume:(double)volume; +- (void)decrementVolume; +- (void)incrementVolume; + +- (void)updatePlayButton; +- (void)togglePlaying:(id)sender; +- (BOOL)playing; +- (void)setPlaying:(BOOL)playing; + +- (void)rewind:(id)sender; +- (void)fastForward:(id)sender; + +- (NSString *)remainingTimeText; +- (NSString *)elapsedTimeText; + +- (void)exitFullscreen:(id)sender; +@end + +@interface WebVideoFullscreenHUDWindow : NSWindow +@end + +@implementation WebVideoFullscreenHUDWindow + +- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag +{ + UNUSED_PARAM(aStyle); + self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:bufferingType defer:flag]; + if (!self) + return nil; + + [self setOpaque:NO]; + [self setBackgroundColor:[NSColor clearColor]]; + [self setLevel:NSPopUpMenuWindowLevel]; + [self setAcceptsMouseMovedEvents:YES]; + [self setIgnoresMouseEvents:NO]; + [self setMovableByWindowBackground:YES]; + + return self; +} + +- (BOOL)canBecomeKeyWindow +{ + return YES; +} + +- (void)cancelOperation:(id)sender +{ + [[self windowController] exitFullscreen:self]; +} + +- (void)center +{ + NSRect hudFrame = [self frame]; + NSRect screenFrame = [[NSScreen mainScreen] frame]; + [self setFrameTopLeftPoint:NSMakePoint(screenFrame.origin.x + (screenFrame.size.width - hudFrame.size.width) / 2, + screenFrame.origin.y + (screenFrame.size.height - hudFrame.size.height) / 6)]; +} + +- (void)keyDown:(NSEvent *)event +{ + [super keyDown:event]; + [[self windowController] fadeWindowIn]; +} + +- (BOOL)resignFirstResponder +{ + return NO; +} + +- (BOOL)performKeyEquivalent:(NSEvent *)event +{ + // Block all command key events while the fullscreen window is up. + if ([event type] != NSKeyDown) + return NO; + + if (!([event modifierFlags] & NSCommandKeyMask)) + return NO; + + return YES; +} + +@end + +static const CGFloat windowHeight = 59; +static const CGFloat windowWidth = 438; + +static const NSTimeInterval HUDWindowFadeOutDelay = 3; + +@implementation WebVideoFullscreenHUDWindowController + +- (id)init +{ + NSWindow *window = [[WebVideoFullscreenHUDWindow alloc] initWithContentRect:NSMakeRect(0, 0, windowWidth, windowHeight) + styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; + self = [super initWithWindow:window]; + [window setDelegate:self]; + [window release]; + if (!self) + return nil; + [self windowDidLoad]; + return self; +} + +- (void)dealloc +{ + ASSERT(!_timelineUpdateTimer); +#if !defined(BUILDING_ON_TIGER) + ASSERT(!_area); +#endif + ASSERT(!_isScrubbing); + [_timeline release]; + [_remainingTimeText release]; + [_elapsedTimeText release]; + [_volumeSlider release]; + [_playButton release]; + [super dealloc]; +} + +#if !defined(BUILDING_ON_TIGER) +- (void)setArea:(NSTrackingArea *)area +{ + if (area == _area) + return; + [_area release]; + _area = [area retain]; +} +#endif + +- (void)keyDown:(NSEvent *)event +{ + NSString *charactersIgnoringModifiers = [event charactersIgnoringModifiers]; + if ([charactersIgnoringModifiers length] == 1) { + switch ([charactersIgnoringModifiers characterAtIndex:0]) { + case ' ': + [self togglePlaying:nil]; + return; + case NSUpArrowFunctionKey: + if ([event modifierFlags] & NSAlternateKeyMask) + [self setVolume:[self maxVolume]]; + else + [self incrementVolume]; + return; + case NSDownArrowFunctionKey: + if ([event modifierFlags] & NSAlternateKeyMask) + [self setVolume:0]; + else + [self decrementVolume]; + return; + default: + break; + } + } + + [super keyDown:event]; +} + +- (id <WebVideoFullscreenHUDWindowControllerDelegate>)delegate +{ + return _delegate; +} + +- (void)setDelegate:(id <WebVideoFullscreenHUDWindowControllerDelegate>)delegate +{ + _delegate = delegate; +} + +- (void)scheduleTimeUpdate +{ + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(unscheduleTimeUpdate) object:self]; + + // First, update right away, then schedule future update + [self updateTime]; + [self updatePlayButton]; + + [_timelineUpdateTimer invalidate]; + [_timelineUpdateTimer release]; + + // Note that this creates a retain cycle between the window and us. + _timelineUpdateTimer = [[NSTimer timerWithTimeInterval:0.25 target:self selector:@selector(updateTime) userInfo:nil repeats:YES] retain]; +#if defined(BUILDING_ON_TIGER) + [[NSRunLoop currentRunLoop] addTimer:_timelineUpdateTimer forMode:(NSString *)kCFRunLoopCommonModes]; +#else + [[NSRunLoop currentRunLoop] addTimer:_timelineUpdateTimer forMode:NSRunLoopCommonModes]; +#endif +} + +- (void)unscheduleTimeUpdate +{ + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(unscheduleTimeUpdate) object:nil]; + + [_timelineUpdateTimer invalidate]; + [_timelineUpdateTimer release]; + _timelineUpdateTimer = nil; +} + +- (void)fadeWindowIn +{ + NSWindow *window = [self window]; + if (![window isVisible]) + [window setAlphaValue:0]; + + [window makeKeyAndOrderFront:self]; +#if defined(BUILDING_ON_TIGER) + [window setAlphaValue:1]; +#else + [[window animator] setAlphaValue:1]; +#endif + [self scheduleTimeUpdate]; + + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(fadeWindowOut) object:nil]; + if (!_mouseIsInHUD && [self playing]) // Don't fade out when paused. + [self performSelector:@selector(fadeWindowOut) withObject:nil afterDelay:HUDWindowFadeOutDelay]; +} + +- (void)fadeWindowOut +{ + [NSCursor setHiddenUntilMouseMoves:YES]; +#if defined(BUILDING_ON_TIGER) + [[self window] setAlphaValue:0]; +#else + [[[self window] animator] setAlphaValue:0]; +#endif + [self performSelector:@selector(unscheduleTimeUpdate) withObject:nil afterDelay:1]; +} + +- (void)closeWindow +{ + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(fadeWindowOut) object:nil]; + [self unscheduleTimeUpdate]; + NSWindow *window = [self window]; +#if !defined(BUILDING_ON_TIGER) + [[window contentView] removeTrackingArea:_area]; + [self setArea:nil]; +#endif + [window close]; + [window setDelegate:nil]; + [self setWindow:nil]; +} + +#ifndef HAVE_MEDIA_CONTROL +enum { + WKMediaUIControlPlayPauseButton, + WKMediaUIControlRewindButton, + WKMediaUIControlFastForwardButton, + WKMediaUIControlExitFullscreenButton, + WKMediaUIControlVolumeDownButton, + WKMediaUIControlSlider, + WKMediaUIControlVolumeUpButton, + WKMediaUIControlTimeline +}; +#endif + +static NSControl *createControlWithMediaUIControlType(int controlType, NSRect frame) +{ +#ifdef HAVE_MEDIA_CONTROL + NSControl *control = WKCreateMediaUIControl(controlType); + [control setFrame:frame]; + return control; +#else + if (controlType == WKMediaUIControlSlider) + return [[NSSlider alloc] initWithFrame:frame]; + return [[NSControl alloc] initWithFrame:frame]; +#endif +} + +static NSTextField *createTimeTextField(NSRect frame) +{ + NSTextField *textField = [[NSTextField alloc] initWithFrame:frame]; + [textField setTextColor:[NSColor whiteColor]]; + [textField setBordered:NO]; + [textField setFont:[NSFont boldSystemFontOfSize:10]]; + [textField setDrawsBackground:NO]; + [textField setBezeled:NO]; + [textField setEditable:NO]; + [textField setSelectable:NO]; + return textField; +} + +- (void)windowDidLoad +{ + static const CGFloat horizontalMargin = 10; + static const CGFloat playButtonWidth = 41; + static const CGFloat playButtonHeight = 35; + static const CGFloat playButtonTopMargin = 4; + static const CGFloat volumeSliderWidth = 50; + static const CGFloat volumeSliderHeight = 13; + static const CGFloat volumeButtonWidth = 18; + static const CGFloat volumeButtonHeight = 16; + static const CGFloat volumeUpButtonLeftMargin = 4; + static const CGFloat volumeControlsTopMargin = 13; + static const CGFloat exitFullscreenButtonWidth = 25; + static const CGFloat exitFullscreenButtonHeight = 21; + static const CGFloat exitFullscreenButtonTopMargin = 11; + static const CGFloat timelineWidth = 315; + static const CGFloat timelineHeight = 14; + static const CGFloat timelineBottomMargin = 7; + static const CGFloat timeTextFieldWidth = 54; + static const CGFloat timeTextFieldHeight = 13; + static const CGFloat timeTextFieldHorizontalMargin = 7; + + NSWindow *window = [self window]; + ASSERT(window); + +#ifdef HAVE_MEDIA_CONTROL + NSView *background = WKCreateMediaUIBackgroundView(); +#else + NSView *background = [[NSView alloc] init]; +#endif + [window setContentView:background]; +#if !defined(BUILDING_ON_TIGER) + _area = [[NSTrackingArea alloc] initWithRect:[background bounds] options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways owner:self userInfo:nil]; + [background addTrackingArea:_area]; +#endif + [background release]; + + NSView *contentView = [window contentView]; + + CGFloat center = webkit_CGFloor((windowWidth - playButtonWidth) / 2); + _playButton = (NSButton *)createControlWithMediaUIControlType(WKMediaUIControlPlayPauseButton, NSMakeRect(center, windowHeight - playButtonTopMargin - playButtonHeight, playButtonWidth, playButtonHeight)); + ASSERT([_playButton isKindOfClass:[NSButton class]]); + [_playButton setTarget:self]; + [_playButton setAction:@selector(togglePlaying:)]; + [contentView addSubview:_playButton]; + + CGFloat closeToRight = windowWidth - horizontalMargin - exitFullscreenButtonWidth; + NSControl *exitFullscreenButton = createControlWithMediaUIControlType(WKMediaUIControlExitFullscreenButton, NSMakeRect(closeToRight, windowHeight - exitFullscreenButtonTopMargin - exitFullscreenButtonHeight, exitFullscreenButtonWidth, exitFullscreenButtonHeight)); + [exitFullscreenButton setAction:@selector(exitFullscreen:)]; + [exitFullscreenButton setTarget:self]; + [contentView addSubview:exitFullscreenButton]; + [exitFullscreenButton release]; + + CGFloat volumeControlsBottom = windowHeight - volumeControlsTopMargin - volumeButtonHeight; + CGFloat left = horizontalMargin; + NSControl *volumeDownButton = createControlWithMediaUIControlType(WKMediaUIControlVolumeDownButton, NSMakeRect(left, volumeControlsBottom, volumeButtonWidth, volumeButtonHeight)); + [contentView addSubview:volumeDownButton]; + [volumeDownButton setTarget:self]; + [volumeDownButton setAction:@selector(setVolumeToZero:)]; + [volumeDownButton release]; + + left += volumeButtonWidth; + _volumeSlider = createControlWithMediaUIControlType(WKMediaUIControlSlider, NSMakeRect(left, volumeControlsBottom + webkit_CGFloor((volumeButtonHeight - volumeSliderHeight) / 2), volumeSliderWidth, volumeSliderHeight)); + [_volumeSlider setValue:[NSNumber numberWithDouble:[self maxVolume]] forKey:@"maxValue"]; + [_volumeSlider setTarget:self]; + [_volumeSlider setAction:@selector(volumeChanged:)]; + [contentView addSubview:_volumeSlider]; + + left += volumeSliderWidth + volumeUpButtonLeftMargin; + NSControl *volumeUpButton = createControlWithMediaUIControlType(WKMediaUIControlVolumeUpButton, NSMakeRect(left, volumeControlsBottom, volumeButtonWidth, volumeButtonHeight)); + [volumeUpButton setTarget:self]; + [volumeUpButton setAction:@selector(setVolumeToMaximum:)]; + [contentView addSubview:volumeUpButton]; + [volumeUpButton release]; + +#ifdef HAVE_MEDIA_CONTROL + _timeline = WKCreateMediaUIControl(WKMediaUIControlTimeline); +#else + _timeline = [[NSSlider alloc] init]; +#endif + [_timeline setTarget:self]; + [_timeline setAction:@selector(timelinePositionChanged:)]; + [_timeline setFrame:NSMakeRect(webkit_CGFloor((windowWidth - timelineWidth) / 2), timelineBottomMargin, timelineWidth, timelineHeight)]; + [contentView addSubview:_timeline]; + + _elapsedTimeText = createTimeTextField(NSMakeRect(timeTextFieldHorizontalMargin, timelineBottomMargin, timeTextFieldWidth, timeTextFieldHeight)); + [_elapsedTimeText setAlignment:NSLeftTextAlignment]; + [contentView addSubview:_elapsedTimeText]; + + _remainingTimeText = createTimeTextField(NSMakeRect(windowWidth - timeTextFieldHorizontalMargin - timeTextFieldWidth, timelineBottomMargin, timeTextFieldWidth, timeTextFieldHeight)); + [_remainingTimeText setAlignment:NSRightTextAlignment]; + [contentView addSubview:_remainingTimeText]; + + [window recalculateKeyViewLoop]; + [window setInitialFirstResponder:_playButton]; + [window center]; +} + +- (void)updateVolume +{ + [_volumeSlider setDoubleValue:[self volume]]; +} + +- (void)updateTime +{ + [self updateVolume]; + + [_timeline setFloatValue:[self currentTime]]; + [_timeline setValue:[NSNumber numberWithDouble:[self duration]] forKey:@"maxValue"]; + + [_remainingTimeText setStringValue:[self remainingTimeText]]; + [_elapsedTimeText setStringValue:[self elapsedTimeText]]; +} + +- (void)endScrubbing +{ + ASSERT(_isScrubbing); + _isScrubbing = NO; + if (HTMLMediaElement* mediaElement = [_delegate mediaElement]) + mediaElement->endScrubbing(); +} + +- (void)timelinePositionChanged:(id)sender +{ + [self setCurrentTime:[_timeline floatValue]]; + if (!_isScrubbing) { + _isScrubbing = YES; + if (HTMLMediaElement* mediaElement = [_delegate mediaElement]) + mediaElement->beginScrubbing(); + static NSArray *endScrubbingModes = [[NSArray alloc] initWithObjects:NSDefaultRunLoopMode, NSModalPanelRunLoopMode, nil]; + // Schedule -endScrubbing for when leaving mouse tracking mode. + [[NSRunLoop currentRunLoop] performSelector:@selector(endScrubbing) target:self argument:nil order:0 modes:endScrubbingModes]; + } +} + +- (float)currentTime +{ + return [_delegate mediaElement] ? [_delegate mediaElement]->currentTime() : 0; +} + +- (void)setCurrentTime:(float)currentTime +{ + if (![_delegate mediaElement]) + return; + WebCore::ExceptionCode e; + [_delegate mediaElement]->setCurrentTime(currentTime, e); + [self updateTime]; +} + +- (double)duration +{ + return [_delegate mediaElement] ? [_delegate mediaElement]->duration() : 0; +} + +- (double)maxVolume +{ + // Set the volume slider resolution + return 100; +} + +- (void)volumeChanged:(id)sender +{ + [self setVolume:[_volumeSlider doubleValue]]; +} + +- (void)setVolumeToZero:(id)sender +{ + [self setVolume:0]; +} + +- (void)setVolumeToMaximum:(id)sender +{ + [self setVolume:[self maxVolume]]; +} + +- (void)decrementVolume +{ + if (![_delegate mediaElement]) + return; + + double volume = [self volume] - 10; + [self setVolume:max(volume, 0.)]; +} + +- (void)incrementVolume +{ + if (![_delegate mediaElement]) + return; + + double volume = [self volume] + 10; + [self setVolume:min(volume, [self maxVolume])]; +} + +- (double)volume +{ + return [_delegate mediaElement] ? [_delegate mediaElement]->volume() * [self maxVolume] : 0; +} + +- (void)setVolume:(double)volume +{ + if (![_delegate mediaElement]) + return; + WebCore::ExceptionCode e; + if ([_delegate mediaElement]->muted()) + [_delegate mediaElement]->setMuted(false); + [_delegate mediaElement]->setVolume(volume / [self maxVolume], e); + [self updateVolume]; +} + +- (void)updatePlayButton +{ + [_playButton setIntValue:[self playing]]; +} + +- (void)updateRate +{ + BOOL playing = [self playing]; + + // Keep the HUD visible when paused. + if (!playing) + [self fadeWindowIn]; + else if (!_mouseIsInHUD) { + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(fadeWindowOut) object:nil]; + [self performSelector:@selector(fadeWindowOut) withObject:nil afterDelay:HUDWindowFadeOutDelay]; + } + [self updatePlayButton]; +} + +- (void)togglePlaying:(id)sender +{ + [self setPlaying:![self playing]]; +} + +- (BOOL)playing +{ + HTMLMediaElement* mediaElement = [_delegate mediaElement]; + if (!mediaElement) + return NO; + + return !mediaElement->canPlay(); +} + +- (void)setPlaying:(BOOL)playing +{ + HTMLMediaElement* mediaElement = [_delegate mediaElement]; + + if (!mediaElement) + return; + + if (playing) + mediaElement->play(mediaElement->processingUserGesture()); + else + mediaElement->pause(mediaElement->processingUserGesture()); +} + +static NSString *timeToString(double time) +{ + ASSERT_ARG(time, time >= 0); + + if (!isfinite(time)) + time = 0; + + int seconds = fabs(time); + int hours = seconds / (60 * 60); + int minutes = (seconds / 60) % 60; + seconds %= 60; + + if (hours) + return [NSString stringWithFormat:@"%d:%02d:%02d", hours, minutes, seconds]; + + return [NSString stringWithFormat:@"%02d:%02d", minutes, seconds]; +} + +- (NSString *)remainingTimeText +{ + HTMLMediaElement* mediaElement = [_delegate mediaElement]; + if (!mediaElement) + return @""; + + return [@"-" stringByAppendingString:timeToString(mediaElement->duration() - mediaElement->currentTime())]; +} + +- (NSString *)elapsedTimeText +{ + if (![_delegate mediaElement]) + return @""; + + return timeToString([_delegate mediaElement]->currentTime()); +} + +// MARK: NSResponder + +- (void)mouseEntered:(NSEvent *)theEvent +{ + // Make sure the HUD won't be hidden from now + _mouseIsInHUD = YES; + [self fadeWindowIn]; +} + +- (void)mouseExited:(NSEvent *)theEvent +{ + _mouseIsInHUD = NO; + [self fadeWindowIn]; +} + +- (void)rewind:(id)sender +{ + if (![_delegate mediaElement]) + return; + [_delegate mediaElement]->rewind(30); +} + +- (void)fastForward:(id)sender +{ + if (![_delegate mediaElement]) + return; +} + +- (void)exitFullscreen:(id)sender +{ + if (_isEndingFullscreen) + return; + _isEndingFullscreen = YES; + [_delegate requestExitFullscreen]; +} + +// MARK: NSWindowDelegate + +- (void)windowDidExpose:(NSNotification *)notification +{ + [self scheduleTimeUpdate]; +} + +- (void)windowDidClose:(NSNotification *)notification +{ + [self unscheduleTimeUpdate]; +} + +@end + +#endif diff --git a/Source/WebKit/mac/WebView/WebView.h b/Source/WebKit/mac/WebView/WebView.h new file mode 100644 index 0000000..17efb5e --- /dev/null +++ b/Source/WebKit/mac/WebView/WebView.h @@ -0,0 +1,836 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * + * 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 <Cocoa/Cocoa.h> + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 +#define WebNSInteger int +#else +#define WebNSInteger NSInteger +#endif + +@class DOMCSSStyleDeclaration; +@class DOMDocument; +@class DOMElement; +@class DOMNode; +@class DOMRange; + +@class WebArchive; +@class WebBackForwardList; +@class WebDataSource; +@class WebFrame; +@class WebFrameView; +@class WebHistoryItem; +@class WebPreferences; +@class WebScriptObject; +@class WebViewPrivate; + +// Element dictionary keys +extern NSString *WebElementDOMNodeKey; // DOMNode of the element +extern NSString *WebElementFrameKey; // WebFrame of the element +extern NSString *WebElementImageAltStringKey; // NSString of the ALT attribute of the image element +extern NSString *WebElementImageKey; // NSImage of the image element +extern NSString *WebElementImageRectKey; // NSValue of an NSRect, the rect of the image element +extern NSString *WebElementImageURLKey; // NSURL of the image element +extern NSString *WebElementIsSelectedKey; // NSNumber of BOOL indicating whether the element is selected or not +extern NSString *WebElementLinkURLKey; // NSURL of the link if the element is within an anchor +extern NSString *WebElementLinkTargetFrameKey; // WebFrame of the target of the anchor +extern NSString *WebElementLinkTitleKey; // NSString of the title of the anchor +extern NSString *WebElementLinkLabelKey; // NSString of the text within the anchor + +/* + @discussion Notifications sent by WebView to mark the progress of loads. + @constant WebViewProgressStartedNotification Posted whenever a load begins in the WebView, including + a load that is initiated in a subframe. After receiving this notification zero or more + WebViewProgressEstimateChangedNotifications will be sent. The userInfo will be nil. + @constant WebViewProgressEstimateChangedNotification Posted whenever the value of + estimatedProgress changes. The userInfo will be nil. + @constant WebViewProgressFinishedNotification Posted when the load for a WebView has finished. + The userInfo will be nil. +*/ +extern NSString *WebViewProgressStartedNotification; +extern NSString *WebViewProgressEstimateChangedNotification; +extern NSString *WebViewProgressFinishedNotification; + +/*! + @class WebView + WebView manages the interaction between WebFrameViews and WebDataSources. Modification + of the policies and behavior of the WebKit is largely managed by WebViews and their + delegates. + + <p> + Typical usage: + </p> + <pre> + WebView *webView; + WebFrame *mainFrame; + + webView = [[WebView alloc] initWithFrame: NSMakeRect (0,0,640,480)]; + mainFrame = [webView mainFrame]; + [mainFrame loadRequest:request]; + </pre> + + WebViews have the following delegates: WebUIDelegate, WebResourceLoadDelegate, + WebFrameLoadDelegate, and WebPolicyDelegate. + + WebKit depends on the WebView's WebUIDelegate for all window + related management, including opening new windows and controlling the user interface + elements in those windows. + + WebResourceLoadDelegate is used to monitor the progress of resources as they are + loaded. This delegate may be used to present users with a progress monitor. + + The WebFrameLoadDelegate receives messages when the URL in a WebFrame is + changed. + + WebView's WebPolicyDelegate can make determinations about how + content should be handled, based on the resource's URL and MIME type. +*/ +@interface WebView : NSView +{ +@private + WebViewPrivate *_private; +} + +/*! + @method canShowMIMEType: + @abstract Checks if the WebKit can show content of a certain MIME type. + @param MIMEType The MIME type to check. + @result YES if the WebKit can show content with MIMEtype. +*/ ++ (BOOL)canShowMIMEType:(NSString *)MIMEType; + + +/*! + @method canShowMIMETypeAsHTML: + @abstract Checks if the the MIME type is a type that the WebKit will interpret as HTML. + @param MIMEType The MIME type to check. + @result YES if the MIMEtype in an HTML type. +*/ ++ (BOOL)canShowMIMETypeAsHTML:(NSString *)MIMEType; + +/*! + @method MIMETypesShownAsHTML + @result Returns an array of NSStrings that describe the MIME types + WebKit will attempt to render as HTML. +*/ ++ (NSArray *)MIMETypesShownAsHTML; + +/*! + @method setMIMETypesShownAsHTML: + @discussion Sets the array of NSString MIME types that WebKit will + attempt to render as HTML. Typically you will retrieve the built-in + array using MIMETypesShownAsHTML and add additional MIME types to that + array. +*/ ++ (void)setMIMETypesShownAsHTML:(NSArray *)MIMETypes; + +/*! + @method URLFromPasteboard: + @abstract Returns a URL from a pasteboard + @param pasteboard The pasteboard with a URL + @result A URL if the pasteboard has one. Nil if it does not. + @discussion This method differs than NSURL's URLFromPasteboard method in that it tries multiple pasteboard types + including NSURLPboardType to find a URL on the pasteboard. +*/ ++ (NSURL *)URLFromPasteboard:(NSPasteboard *)pasteboard; + +/*! + @method URLTitleFromPasteboard: + @abstract Returns a URL title from a pasteboard + @param pasteboard The pasteboard with a URL title + @result A URL title if the pasteboard has one. Nil if it does not. + @discussion This method returns a title that refers a URL on the pasteboard. An example of this is the link label + which is the text inside the anchor tag. +*/ ++ (NSString *)URLTitleFromPasteboard:(NSPasteboard *)pasteboard; + +/*! + @method registerURLSchemeAsLocal: + @abstract Adds the scheme to the list of schemes to be treated as local. + @param scheme The scheme to register +*/ ++ (void)registerURLSchemeAsLocal:(NSString *)scheme; + +/*! + @method initWithFrame:frameName:groupName: + @abstract The designated initializer for WebView. + @discussion Initialize a WebView with the supplied parameters. This method will + create a main WebFrame with the view. Passing a top level frame name is useful if you + handle a targetted frame navigation that would normally open a window in some other + way that still ends up creating a new WebView. + @param frame The frame used to create the view. + @param frameName The name to use for the top level frame. May be nil. + @param groupName The name of the webView set to which this webView will be added. May be nil. + @result Returns an initialized WebView. +*/ +- (id)initWithFrame:(NSRect)frame frameName:(NSString *)frameName groupName:(NSString *)groupName; + +/*! + @method close + @abstract Closes the receiver, unloading its web page and canceling any pending loads. + Once the receiver has closed, it will no longer respond to requests or fire delegate methods. + (However, the -close method itself may fire delegate methods.) + @discussion A garbage collected application is required to call close when the receiver is no longer needed. + The close method will be called automatically when the window or hostWindow closes and shouldCloseWithWindow returns YES. + A non-garbage collected application can still call close, providing a convenient way to prevent receiver + from doing any more loading and firing any future delegate methods. +*/ +- (void)close; + +/*! + @method setShouldCloseWithWindow: + @abstract Set whether the receiver closes when either it's window or hostWindow closes. + @param close YES if the receiver should close when either it's window or hostWindow closes, otherwise NO. +*/ +- (void)setShouldCloseWithWindow:(BOOL)close; + +/*! + @method shouldCloseWithWindow + @abstract Returns whether the receiver closes when either it's window or hostWindow closes. + @discussion Defaults to YES in garbage collected applications, otherwise NO to maintain backwards compatibility. + @result YES if the receiver closes when either it's window or hostWindow closes, otherwise NO. +*/ +- (BOOL)shouldCloseWithWindow; + +/*! + @method setUIDelegate: + @abstract Set the WebView's WebUIDelegate. + @param delegate The WebUIDelegate to set as the delegate. +*/ +- (void)setUIDelegate:(id)delegate; + +/*! + @method UIDelegate + @abstract Return the WebView's WebUIDelegate. + @result The WebView's WebUIDelegate. +*/ +- (id)UIDelegate; + +/*! + @method setResourceLoadDelegate: + @abstract Set the WebView's WebResourceLoadDelegate load delegate. + @param delegate The WebResourceLoadDelegate to set as the load delegate. +*/ +- (void)setResourceLoadDelegate:(id)delegate; + +/*! + @method resourceLoadDelegate + @result Return the WebView's WebResourceLoadDelegate. +*/ +- (id)resourceLoadDelegate; + +/*! + @method setDownloadDelegate: + @abstract Set the WebView's WebDownloadDelegate. + @discussion The download delegate is retained by WebDownload when any downloads are in progress. + @param delegate The WebDownloadDelegate to set as the download delegate. +*/ +- (void)setDownloadDelegate:(id)delegate; + +/*! + @method downloadDelegate + @abstract Return the WebView's WebDownloadDelegate. + @result The WebView's WebDownloadDelegate. +*/ +- (id)downloadDelegate; + +/*! + @method setFrameLoadDelegate: + @abstract Set the WebView's WebFrameLoadDelegate delegate. + @param delegate The WebFrameLoadDelegate to set as the delegate. +*/ +- (void)setFrameLoadDelegate:(id)delegate; + +/*! + @method frameLoadDelegate + @abstract Return the WebView's WebFrameLoadDelegate delegate. + @result The WebView's WebFrameLoadDelegate delegate. +*/ +- (id)frameLoadDelegate; + +/*! + @method setPolicyDelegate: + @abstract Set the WebView's WebPolicyDelegate delegate. + @param delegate The WebPolicyDelegate to set as the delegate. +*/ +- (void)setPolicyDelegate:(id)delegate; + +/*! + @method policyDelegate + @abstract Return the WebView's WebPolicyDelegate. + @result The WebView's WebPolicyDelegate. +*/ +- (id)policyDelegate; + +/*! + @method mainFrame + @abstract Return the top level frame. + @discussion Note that even document that are not framesets will have a + mainFrame. + @result The main frame. +*/ +- (WebFrame *)mainFrame; + +/*! + @method selectedFrame + @abstract Return the frame that has the active selection. + @discussion Returns the frame that contains the first responder, if any. Otherwise returns the + frame that contains a non-zero-length selection, if any. Returns nil if no frame meets these criteria. + @result The selected frame. +*/ +- (WebFrame *)selectedFrame; + +/*! + @method backForwardList + @result The backforward list for this webView. +*/ +- (WebBackForwardList *)backForwardList; + +/*! + @method setMaintainsBackForwardList: + @abstract Enable or disable the use of a backforward list for this webView. + @param flag Turns use of the back forward list on or off +*/ +- (void)setMaintainsBackForwardList:(BOOL)flag; + +/*! + @method goBack + @abstract Go back to the previous URL in the backforward list. + @result YES if able to go back in the backforward list, NO otherwise. +*/ +- (BOOL)goBack; + +/*! + @method goForward + @abstract Go forward to the next URL in the backforward list. + @result YES if able to go forward in the backforward list, NO otherwise. +*/ +- (BOOL)goForward; + +/*! + @method goToBackForwardItem: + @abstract Go back or forward to an item in the backforward list. + @result YES if able to go to the item, NO otherwise. +*/ +- (BOOL)goToBackForwardItem:(WebHistoryItem *)item; + +/*! + @method setTextSizeMultiplier: + @abstract Change the size of the text rendering in views managed by this webView. + @param multiplier A fractional percentage value, 1.0 is 100%. +*/ +- (void)setTextSizeMultiplier:(float)multiplier; + +/*! + @method textSizeMultiplier + @result The text size multipler. +*/ +- (float)textSizeMultiplier; + +/*! + @method setApplicationNameForUserAgent: + @abstract Set the application name. + @discussion This name will be used in user-agent strings + that are chosen for best results in rendering web pages. + @param applicationName The application name +*/ +- (void)setApplicationNameForUserAgent:(NSString *)applicationName; + +/*! + @method applicationNameForUserAgent + @result The name of the application as used in the user-agent string. +*/ +- (NSString *)applicationNameForUserAgent; + +/*! + @method setCustomUserAgent: + @abstract Set the user agent. + @discussion Setting this means that the webView should use this user-agent string + instead of constructing a user-agent string for each URL. Setting it to nil + causes the webView to construct the user-agent string for each URL + for best results rendering web pages. + @param userAgentString The user agent description +*/ +- (void)setCustomUserAgent:(NSString *)userAgentString; + +/*! + @method customUserAgent + @result The custom user-agent string or nil if no custom user-agent string has been set. +*/ +- (NSString *)customUserAgent; + +/*! + @method userAgentForURL: + @abstract Get the appropriate user-agent string for a particular URL. + @param URL The URL. + @result The user-agent string for the supplied URL. +*/ +- (NSString *)userAgentForURL:(NSURL *)URL; + + +/*! + @method supportsTextEncoding + @abstract Find out if the current web page supports text encodings. + @result YES if the document view of the current web page can + support different text encodings. +*/ +- (BOOL)supportsTextEncoding; + +/*! + @method setCustomTextEncodingName: + @discussion Make the page display with a different text encoding; stops any load in progress. + The text encoding passed in overrides the normal text encoding smarts including + what's specified in a web page's header or HTTP response. + The text encoding automatically goes back to the default when the top level frame + changes to a new location. + Setting the text encoding name to nil makes the webView use default encoding rules. + @param encoding The text encoding name to use to display a page or nil. +*/ +- (void)setCustomTextEncodingName:(NSString *)encodingName; + +/*! + @method customTextEncodingName + @result The custom text encoding name or nil if no custom text encoding name has been set. +*/ +- (NSString *)customTextEncodingName; + +/*! + @method setMediaStyle: + @discussion Set the media style for the WebView. The mediaStyle will override the normal value + of the CSS media property. Setting the value to nil will restore the normal value. + @param mediaStyle The value to use for the CSS media property. +*/ +- (void)setMediaStyle:(NSString *)mediaStyle; + +/*! + @method mediaStyle + @result mediaStyle The value to use for the CSS media property, as set by setMediaStyle:. It + will be nil unless set by that method. +*/ +- (NSString *)mediaStyle; + +/*! + @method stringByEvaluatingJavaScriptFromString: + @param script The text of the JavaScript. + @result The result of the script, converted to a string, or nil for failure. +*/ +- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script; + +/*! + @method windowScriptObject + @discussion windowScriptObject return a WebScriptObject that represents the + window object from the script environment. + @result Returns the window object from the script environment. +*/ +- (WebScriptObject *)windowScriptObject; + +/*! + @method setPreferences: + @param preferences The preferences to use for the webView. + @abstract Override the standard setting for the webView. +*/ +- (void)setPreferences: (WebPreferences *)prefs; + +/*! + @method preferences + @result Returns the preferences used by this webView. + @discussion This method will return [WebPreferences standardPreferences] if no + other instance of WebPreferences has been set. +*/ +- (WebPreferences *)preferences; + +/*! + @method setPreferencesIdentifier: + @param anIdentifier The string to use a prefix for storing values for this WebView in the user + defaults database. + @discussion If the WebPreferences for this WebView are stored in the user defaults database, the + string set in this method will be used a key prefix. +*/ +- (void)setPreferencesIdentifier:(NSString *)anIdentifier; + +/*! + @method preferencesIdentifier + @result Returns the WebPreferences key prefix. +*/ +- (NSString *)preferencesIdentifier; + + +/*! + @method setHostWindow: + @param hostWindow The host window for the web view. + @discussion Parts of WebKit (such as plug-ins and JavaScript) depend on a window to function + properly. Set a host window so these parts continue to function even when the web view is + not in an actual window. +*/ +- (void)setHostWindow:(NSWindow *)hostWindow; + +/*! + @method hostWindow + @result The host window for the web view. +*/ +- (NSWindow *)hostWindow; + +/*! + @method searchFor:direction:caseSensitive: + @abstract Searches a document view for a string and highlights the string if it is found. + Starts the search from the current selection. Will search across all frames. + @param string The string to search for. + @param forward YES to search forward, NO to seach backwards. + @param caseFlag YES to for case-sensitive search, NO for case-insensitive search. + @result YES if found, NO if not found. +*/ +- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag; + +/*! + @method registerViewClass:representationClass:forMIMEType: + @discussion Register classes that implement WebDocumentView and WebDocumentRepresentation respectively. + A document class may register for a primary MIME type by excluding + a subtype, i.e. "video/" will match the document class with + all video types. More specific matching takes precedence + over general matching. + @param viewClass The WebDocumentView class to use to render data for a given MIME type. + @param representationClass The WebDocumentRepresentation class to use to represent data of the given MIME type. + @param MIMEType The MIME type to represent with an object of the given class. +*/ ++ (void)registerViewClass:(Class)viewClass representationClass:(Class)representationClass forMIMEType:(NSString *)MIMEType; + + +/*! + @method setGroupName: + @param groupName The name of the group for this WebView. + @discussion JavaScript may access named frames within the same group. +*/ +- (void)setGroupName:(NSString *)groupName; + +/*! + @method groupName + @discussion The group name for this WebView. +*/ +- (NSString *)groupName; + +/*! + @method estimatedProgress + @discussion An estimate of the percent complete for a document load. This + value will range from 0 to 1.0 and, once a load completes, will remain at 1.0 + until a new load starts, at which point it will be reset to 0. The value is an + estimate based on the total number of bytes expected to be received + for a document, including all it's possible subresources. For more accurate progress + indication it is recommended that you implement a WebFrameLoadDelegate and a + WebResourceLoadDelegate. +*/ +- (double)estimatedProgress; + +/*! + @method isLoading + @discussion Returns YES if there are any pending loads. +*/ +- (BOOL)isLoading; + +/*! + @method elementAtPoint: + @param point A point in the coordinates of the WebView + @result An element dictionary describing the point +*/ +- (NSDictionary *)elementAtPoint:(NSPoint)point; + +/*! + @method pasteboardTypesForSelection + @abstract Returns the pasteboard types that WebView can use for the current selection +*/ +- (NSArray *)pasteboardTypesForSelection; + +/*! + @method writeSelectionWithPasteboardTypes:toPasteboard: + @abstract Writes the current selection to the pasteboard + @param types The types that WebView will write to the pasteboard + @param pasteboard The pasteboard to write to +*/ +- (void)writeSelectionWithPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard; + +/*! + @method pasteboardTypesForElement: + @abstract Returns the pasteboard types that WebView can use for an element + @param element The element +*/ +- (NSArray *)pasteboardTypesForElement:(NSDictionary *)element; + +/*! + @method writeElement:withPasteboardTypes:toPasteboard: + @abstract Writes an element to the pasteboard + @param element The element to write to the pasteboard + @param types The types that WebView will write to the pasteboard + @param pasteboard The pasteboard to write to +*/ +- (void)writeElement:(NSDictionary *)element withPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard; + +/*! + @method moveDragCaretToPoint: + @param point A point in the coordinates of the WebView + @discussion This method moves the caret that shows where something being dragged will be dropped. It may cause the WebView to scroll + to make the new position of the drag caret visible. +*/ +- (void)moveDragCaretToPoint:(NSPoint)point; + +/*! + @method removeDragCaret + @abstract Removes the drag caret from the WebView +*/ +- (void)removeDragCaret; + +/*! + @method setDrawsBackground: + @param drawsBackround YES to cause the receiver to draw a default white background, NO otherwise. + @abstract Sets whether the receiver draws a default white background when the loaded page has no background specified. +*/ +- (void)setDrawsBackground:(BOOL)drawsBackround; + +/*! + @method drawsBackground + @result Returns YES if the receiver draws a default white background, NO otherwise. +*/ +- (BOOL)drawsBackground; + +/*! + @method setShouldUpdateWhileOffscreen: + @abstract Sets whether the receiver must update even when it is not in a window that is currently visible. + @param updateWhileOffscreen whether the receiver is required to render updates to the web page when it is not in a visible window. + @abstract If set to NO, then whenever the web view is not in a visible window, updates to the web page will not necessarily be rendered in the view. + However, when the window is made visible, the view will be updated automatically. Not updating while hidden can improve performance. If set to is YES, + hidden web views are always updated. This is the default. +*/ +- (void)setShouldUpdateWhileOffscreen:(BOOL)updateWhileOffscreen; + +/*! + @method shouldUpdateWhileOffscreen + @result Returns whether the web view is always updated even when it is not in a window that is currently visible. +*/ +- (BOOL)shouldUpdateWhileOffscreen; + +/*! + @method setMainFrameURL: + @param URLString The URL to load in the mainFrame. +*/ +- (void)setMainFrameURL:(NSString *)URLString; + +/*! + @method mainFrameURL + @result Returns the main frame's current URL. +*/ +- (NSString *)mainFrameURL; + +/*! + @method mainFrameDocument + @result Returns the main frame's DOMDocument. +*/ +- (DOMDocument *)mainFrameDocument; + +/*! + @method mainFrameTitle + @result Returns the main frame's title if any, otherwise an empty string. +*/ +- (NSString *)mainFrameTitle; + +/*! + @method mainFrameIcon + @discussion The methods returns the site icon for the current page loaded in the mainFrame. + @result Returns the main frame's icon if any, otherwise nil. +*/ +- (NSImage *)mainFrameIcon; + +@end + + +@interface WebView (WebIBActions) <NSUserInterfaceValidations> +- (IBAction)takeStringURLFrom:(id)sender; +- (IBAction)stopLoading:(id)sender; +- (IBAction)reload:(id)sender; +- (IBAction)reloadFromOrigin:(id)sender; +- (BOOL)canGoBack; +- (IBAction)goBack:(id)sender; +- (BOOL)canGoForward; +- (IBAction)goForward:(id)sender; +- (BOOL)canMakeTextLarger; +- (IBAction)makeTextLarger:(id)sender; +- (BOOL)canMakeTextSmaller; +- (IBAction)makeTextSmaller:(id)sender; +- (BOOL)canMakeTextStandardSize; +- (IBAction)makeTextStandardSize:(id)sender; +- (IBAction)toggleContinuousSpellChecking:(id)sender; +- (IBAction)toggleSmartInsertDelete:(id)sender; +@end + + +// WebView editing support + +extern NSString * const WebViewDidBeginEditingNotification; +extern NSString * const WebViewDidChangeNotification; +extern NSString * const WebViewDidEndEditingNotification; +extern NSString * const WebViewDidChangeTypingStyleNotification; +extern NSString * const WebViewDidChangeSelectionNotification; + +@interface WebView (WebViewCSS) +- (DOMCSSStyleDeclaration *)computedStyleForElement:(DOMElement *)element pseudoElement:(NSString *)pseudoElement; +@end + +@interface WebView (WebViewEditing) +- (DOMRange *)editableDOMRangeForPoint:(NSPoint)point; +- (void)setSelectedDOMRange:(DOMRange *)range affinity:(NSSelectionAffinity)selectionAffinity; +- (DOMRange *)selectedDOMRange; +- (NSSelectionAffinity)selectionAffinity; +- (BOOL)maintainsInactiveSelection; +- (void)setEditable:(BOOL)flag; +- (BOOL)isEditable; +- (void)setTypingStyle:(DOMCSSStyleDeclaration *)style; +- (DOMCSSStyleDeclaration *)typingStyle; +- (void)setSmartInsertDeleteEnabled:(BOOL)flag; +- (BOOL)smartInsertDeleteEnabled; +- (void)setContinuousSpellCheckingEnabled:(BOOL)flag; +- (BOOL)isContinuousSpellCheckingEnabled; +- (WebNSInteger)spellCheckerDocumentTag; +- (NSUndoManager *)undoManager; +- (void)setEditingDelegate:(id)delegate; +- (id)editingDelegate; +- (DOMCSSStyleDeclaration *)styleDeclarationWithText:(NSString *)text; +@end + +@interface WebView (WebViewUndoableEditing) +- (void)replaceSelectionWithNode:(DOMNode *)node; +- (void)replaceSelectionWithText:(NSString *)text; +- (void)replaceSelectionWithMarkupString:(NSString *)markupString; +- (void)replaceSelectionWithArchive:(WebArchive *)archive; +- (void)deleteSelection; +- (void)applyStyle:(DOMCSSStyleDeclaration *)style; +@end + +@interface WebView (WebViewEditingActions) + +- (void)copy:(id)sender; +- (void)cut:(id)sender; +- (void)paste:(id)sender; +- (void)copyFont:(id)sender; +- (void)pasteFont:(id)sender; +- (void)delete:(id)sender; +- (void)pasteAsPlainText:(id)sender; +- (void)pasteAsRichText:(id)sender; + +- (void)changeFont:(id)sender; +- (void)changeAttributes:(id)sender; +- (void)changeDocumentBackgroundColor:(id)sender; +- (void)changeColor:(id)sender; + +- (void)alignCenter:(id)sender; +- (void)alignJustified:(id)sender; +- (void)alignLeft:(id)sender; +- (void)alignRight:(id)sender; + +- (void)checkSpelling:(id)sender; +- (void)showGuessPanel:(id)sender; +- (void)performFindPanelAction:(id)sender; + +- (void)startSpeaking:(id)sender; +- (void)stopSpeaking:(id)sender; + +- (void)moveToBeginningOfSentence:(id)sender; +- (void)moveToBeginningOfSentenceAndModifySelection:(id)sender; +- (void)moveToEndOfSentence:(id)sender; +- (void)moveToEndOfSentenceAndModifySelection:(id)sender; +- (void)selectSentence:(id)sender; + +/* +The following methods are declared in NSResponder.h. +WebView overrides each method in this list, providing +a custom implementation for each. + +- (void)capitalizeWord:(id)sender; +- (void)centerSelectionInVisibleArea:(id)sender; +- (void)changeCaseOfLetter:(id)sender; +- (void)complete:(id)sender; +- (void)deleteBackward:(id)sender; +- (void)deleteBackwardByDecomposingPreviousCharacter:(id)sender; +- (void)deleteForward:(id)sender; +- (void)deleteToBeginningOfLine:(id)sender; +- (void)deleteToBeginningOfParagraph:(id)sender; +- (void)deleteToEndOfLine:(id)sender; +- (void)deleteToEndOfParagraph:(id)sender; +- (void)deleteWordBackward:(id)sender; +- (void)deleteWordForward:(id)sender; +- (void)indent:(id)sender; +- (void)insertBacktab:(id)sender; +- (void)insertNewline:(id)sender; +- (void)insertParagraphSeparator:(id)sender; +- (void)insertTab:(id)sender; +- (void)lowercaseWord:(id)sender; +- (void)moveBackward:(id)sender; +- (void)moveBackwardAndModifySelection:(id)sender; +- (void)moveDown:(id)sender; +- (void)moveDownAndModifySelection:(id)sender; +- (void)moveForward:(id)sender; +- (void)moveForwardAndModifySelection:(id)sender; +- (void)moveLeft:(id)sender; +- (void)moveLeftAndModifySelection:(id)sender; +- (void)moveRight:(id)sender; +- (void)moveRightAndModifySelection:(id)sender; +- (void)moveToBeginningOfDocument:(id)sender; +- (void)moveToBeginningOfDocumentAndModifySelection:(id)sender; +- (void)moveToBeginningOfLine:(id)sender; +- (void)moveToBeginningOfLineAndModifySelection:(id)sender; +- (void)moveToBeginningOfParagraph:(id)sender; +- (void)moveToBeginningOfParagraphAndModifySelection:(id)sender; +- (void)moveToEndOfDocument:(id)sender; +- (void)moveToEndOfDocumentAndModifySelection:(id)sender; +- (void)moveToEndOfLine:(id)sender; +- (void)moveToEndOfLineAndModifySelection:(id)sender; +- (void)moveToEndOfParagraph:(id)sender; +- (void)moveToEndOfParagraphAndModifySelection:(id)sender; +- (void)moveUp:(id)sender; +- (void)moveUpAndModifySelection:(id)sender; +- (void)moveWordBackward:(id)sender; +- (void)moveWordBackwardAndModifySelection:(id)sender; +- (void)moveWordForward:(id)sender; +- (void)moveWordForwardAndModifySelection:(id)sender; +- (void)moveWordLeft:(id)sender; +- (void)moveWordLeftAndModifySelection:(id)sender; +- (void)moveWordRight:(id)sender; +- (void)moveWordRightAndModifySelection:(id)sender; +- (void)pageDown:(id)sender; +- (void)pageUp:(id)sender; +- (void)scrollLineDown:(id)sender; +- (void)scrollLineUp:(id)sender; +- (void)scrollPageDown:(id)sender; +- (void)scrollPageUp:(id)sender; +- (void)selectAll:(id)sender; +- (void)selectLine:(id)sender; +- (void)selectParagraph:(id)sender; +- (void)selectWord:(id)sender; +- (void)uppercaseWord:(id)sender; +*/ + +@end + +#undef WebNSInteger diff --git a/Source/WebKit/mac/WebView/WebView.mm b/Source/WebKit/mac/WebView/WebView.mm new file mode 100644 index 0000000..809b286 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebView.mm @@ -0,0 +1,6280 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 David Smith (catfish.man@gmail.com) + * Copyright (C) 2010 Igalia S.L + * + * 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 "WebViewData.h" + +#import "DOMCSSStyleDeclarationInternal.h" +#import "DOMNodeInternal.h" +#import "DOMRangeInternal.h" +#import "WebApplicationCache.h" +#import "WebBackForwardListInternal.h" +#import "WebBaseNetscapePluginView.h" +#import "WebCache.h" +#import "WebChromeClient.h" +#import "WebContextMenuClient.h" +#import "WebDOMOperationsPrivate.h" +#import "WebDataSourceInternal.h" +#import "WebDatabaseManagerInternal.h" +#import "WebDefaultEditingDelegate.h" +#import "WebDefaultPolicyDelegate.h" +#import "WebDefaultUIDelegate.h" +#import "WebDelegateImplementationCaching.h" +#import "WebDeviceOrientationClient.h" +#import "WebDeviceOrientationProvider.h" +#import "WebDocument.h" +#import "WebDocumentInternal.h" +#import "WebDownload.h" +#import "WebDownloadInternal.h" +#import "WebDragClient.h" +#import "WebDynamicScrollBarsViewInternal.h" +#import "WebEditingDelegate.h" +#import "WebEditorClient.h" +#import "WebFormDelegatePrivate.h" +#import "WebFrameInternal.h" +#import "WebFrameViewInternal.h" +#import "WebFullScreenController.h" +#import "WebGeolocationClient.h" +#import "WebGeolocationPositionInternal.h" +#import "WebHTMLRepresentation.h" +#import "WebHTMLViewInternal.h" +#import "WebHistoryItemInternal.h" +#import "WebIconDatabaseInternal.h" +#import "WebInspector.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 "WebNodeHighlight.h" +#import "WebPDFView.h" +#import "WebPanelAuthenticationHandler.h" +#import "WebPlatformStrategies.h" +#import "WebPluginDatabase.h" +#import "WebPluginHalterClient.h" +#import "WebPolicyDelegate.h" +#import "WebPreferenceKeysPrivate.h" +#import "WebPreferencesPrivate.h" +#import "WebScriptDebugDelegate.h" +#import "WebScriptWorldInternal.h" +#import "WebSystemInterface.h" +#import "WebTextCompletionController.h" +#import "WebTextIterator.h" +#import "WebUIDelegate.h" +#import "WebUIDelegatePrivate.h" +#import "WebVideoFullscreenController.h" +#import <CoreFoundation/CFSet.h> +#import <Foundation/NSURLConnection.h> +#import <JavaScriptCore/APICast.h> +#import <JavaScriptCore/JSValueRef.h> +#import <WebCore/AbstractDatabase.h> +#import <WebCore/ApplicationCacheStorage.h> +#import <WebCore/BackForwardListImpl.h> +#import <WebCore/MemoryCache.h> +#import <WebCore/ColorMac.h> +#import <WebCore/CSSComputedStyleDeclaration.h> +#import <WebCore/Cursor.h> +#import <WebCore/Document.h> +#import <WebCore/DocumentLoader.h> +#import <WebCore/DragController.h> +#import <WebCore/DragData.h> +#import <WebCore/Editor.h> +#import <WebCore/EventHandler.h> +#import <WebCore/ExceptionHandlers.h> +#import <WebCore/FocusController.h> +#import <WebCore/Frame.h> +#import <WebCore/FrameLoader.h> +#import <WebCore/FrameTree.h> +#import <WebCore/FrameView.h> +#import <WebCore/GCController.h> +#import <WebCore/HTMLMediaElement.h> +#import <WebCore/HTMLNames.h> +#import <WebCore/HistoryItem.h> +#import <WebCore/IconDatabase.h> +#import <WebCore/JSCSSStyleDeclaration.h> +#import <WebCore/JSDocument.h> +#import <WebCore/JSElement.h> +#import <WebCore/JSNodeList.h> +#import <WebCore/Logging.h> +#import <WebCore/MIMETypeRegistry.h> +#import <WebCore/NodeList.h> +#import <WebCore/Page.h> +#import <WebCore/PageCache.h> +#import <WebCore/PageGroup.h> +#import <WebCore/PlatformMouseEvent.h> +#import <WebCore/ProgressTracker.h> +#import <WebCore/RenderView.h> +#import <WebCore/RenderWidget.h> +#import <WebCore/ResourceHandle.h> +#import <WebCore/ResourceLoadScheduler.h> +#import <WebCore/RuntimeApplicationChecks.h> +#import <WebCore/SchemeRegistry.h> +#import <WebCore/ScriptController.h> +#import <WebCore/ScriptValue.h> +#import <WebCore/SecurityOrigin.h> +#import <WebCore/SelectionController.h> +#import <WebCore/Settings.h> +#import <WebCore/TextResourceDecoder.h> +#import <WebCore/ThreadCheck.h> +#import <WebCore/WebCoreObjCExtras.h> +#import <WebCore/WebCoreView.h> +#import <WebCore/Widget.h> +#import <WebKit/DOM.h> +#import <WebKit/DOMExtensions.h> +#import <WebKit/DOMPrivate.h> +#import <WebKitSystemInterface.h> +#import <mach-o/dyld.h> +#import <objc/objc-auto.h> +#import <objc/objc-runtime.h> +#import <runtime/ArrayPrototype.h> +#import <runtime/DateInstance.h> +#import <runtime/InitializeThreading.h> +#import <runtime/JSLock.h> +#import <runtime/JSValue.h> +#import <wtf/Assertions.h> +#import <wtf/HashTraits.h> +#import <wtf/RefCountedLeakCounter.h> +#import <wtf/RefPtr.h> +#import <wtf/StdLibExtras.h> +#import <wtf/Threading.h> + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) +#import <AppKit/NSTextChecker.h> +#endif + +#if ENABLE(DASHBOARD_SUPPORT) +#import <WebKit/WebDashboardRegion.h> +#endif + +#if ENABLE(CLIENT_BASED_GEOLOCATION) +#import <WebCore/GeolocationController.h> +#import <WebCore/GeolocationError.h> +#endif + +#if ENABLE(GLIB_SUPPORT) +#import <glib.h> +#endif + +@interface NSSpellChecker (WebNSSpellCheckerDetails) +- (void)_preflightChosenSpellServer; +@end + +@interface NSView (WebNSViewDetails) +- (NSView *)_hitTest:(NSPoint *)aPoint dragTypes:(NSSet *)types; +- (void)_autoscrollForDraggingInfo:(id)dragInfo timeDelta:(NSTimeInterval)repeatDelta; +- (BOOL)_shouldAutoscrollForDraggingInfo:(id)dragInfo; +@end + +@interface NSWindow (WebNSWindowDetails) +- (id)_oldFirstResponderBeforeBecoming; +- (void)_enableScreenUpdatesIfNeeded; +- (BOOL)_wrapsCarbonWindow; +@end + +using namespace WebCore; +using namespace JSC; + +#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(changeBaseWritingDirection) \ +macro(changeBaseWritingDirectionToLTR) \ +macro(changeBaseWritingDirectionToRTL) \ +macro(changeColor) \ +macro(changeDocumentBackgroundColor) \ +macro(changeFont) \ +macro(changeSpelling) \ +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(deleteToMark) \ +macro(deleteWordBackward) \ +macro(deleteWordForward) \ +macro(ignoreSpelling) \ +macro(indent) \ +macro(insertBacktab) \ +macro(insertLineBreak) \ +macro(insertNewline) \ +macro(insertNewlineIgnoringFieldEditor) \ +macro(insertParagraphSeparator) \ +macro(insertTab) \ +macro(insertTabIgnoringFieldEditor) \ +macro(lowercaseWord) \ +macro(makeBaseWritingDirectionLeftToRight) \ +macro(makeBaseWritingDirectionRightToLeft) \ +macro(makeTextWritingDirectionLeftToRight) \ +macro(makeTextWritingDirectionNatural) \ +macro(makeTextWritingDirectionRightToLeft) \ +macro(moveBackward) \ +macro(moveBackwardAndModifySelection) \ +macro(moveDown) \ +macro(moveDownAndModifySelection) \ +macro(moveForward) \ +macro(moveForwardAndModifySelection) \ +macro(moveLeft) \ +macro(moveLeftAndModifySelection) \ +macro(moveParagraphBackwardAndModifySelection) \ +macro(moveParagraphForwardAndModifySelection) \ +macro(moveRight) \ +macro(moveRightAndModifySelection) \ +macro(moveToBeginningOfDocument) \ +macro(moveToBeginningOfDocumentAndModifySelection) \ +macro(moveToBeginningOfLine) \ +macro(moveToBeginningOfLineAndModifySelection) \ +macro(moveToBeginningOfParagraph) \ +macro(moveToBeginningOfParagraphAndModifySelection) \ +macro(moveToBeginningOfSentence) \ +macro(moveToBeginningOfSentenceAndModifySelection) \ +macro(moveToEndOfDocument) \ +macro(moveToEndOfDocumentAndModifySelection) \ +macro(moveToEndOfLine) \ +macro(moveToEndOfLineAndModifySelection) \ +macro(moveToEndOfParagraph) \ +macro(moveToEndOfParagraphAndModifySelection) \ +macro(moveToEndOfSentence) \ +macro(moveToEndOfSentenceAndModifySelection) \ +macro(moveToLeftEndOfLine) \ +macro(moveToLeftEndOfLineAndModifySelection) \ +macro(moveToRightEndOfLine) \ +macro(moveToRightEndOfLineAndModifySelection) \ +macro(moveUp) \ +macro(moveUpAndModifySelection) \ +macro(moveWordBackward) \ +macro(moveWordBackwardAndModifySelection) \ +macro(moveWordForward) \ +macro(moveWordForwardAndModifySelection) \ +macro(moveWordLeft) \ +macro(moveWordLeftAndModifySelection) \ +macro(moveWordRight) \ +macro(moveWordRightAndModifySelection) \ +macro(outdent) \ +macro(orderFrontSubstitutionsPanel) \ +macro(pageDown) \ +macro(pageDownAndModifySelection) \ +macro(pageUp) \ +macro(pageUpAndModifySelection) \ +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(selectLine) \ +macro(selectParagraph) \ +macro(selectSentence) \ +macro(selectToMark) \ +macro(selectWord) \ +macro(setMark) \ +macro(showGuessPanel) \ +macro(startSpeaking) \ +macro(stopSpeaking) \ +macro(subscript) \ +macro(superscript) \ +macro(swapWithMark) \ +macro(takeFindStringFromSelection) \ +macro(toggleBaseWritingDirection) \ +macro(transpose) \ +macro(underline) \ +macro(unscript) \ +macro(uppercaseWord) \ +macro(yank) \ +macro(yankAndSelect) \ + +#define WebKitOriginalTopPrintingMarginKey @"WebKitOriginalTopMargin" +#define WebKitOriginalBottomPrintingMarginKey @"WebKitOriginalBottomMargin" + +#define KeyboardUIModeDidChangeNotification @"com.apple.KeyboardUIModeDidChange" +#define AppleKeyboardUIMode CFSTR("AppleKeyboardUIMode") +#define UniversalAccessDomain CFSTR("com.apple.universalaccess") + +static BOOL s_didSetCacheModel; +static WebCacheModel s_cacheModel = WebCacheModelDocumentViewer; + +#ifndef NDEBUG +static const char webViewIsOpen[] = "At least one WebView is still open."; +#endif + +@interface NSObject (WebValidateWithoutDelegate) +- (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 WebView (WebFileInternal) +- (BOOL)_isLoading; +- (WebFrameView *)_frameViewAtWindowPoint:(NSPoint)point; +- (WebFrame *)_focusedFrame; ++ (void)_preflightSpellChecker; +- (BOOL)_continuousCheckingAllowed; +- (NSResponder *)_responderForResponderOperations; +#if USE(ACCELERATED_COMPOSITING) +- (void)_clearLayerSyncLoopObserver; +#endif +#if ENABLE(GLIB_SUPPORT) +- (void)_clearGlibLoopObserver; +#endif +@end + +static void patchMailRemoveAttributesMethod(); + +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 *WebElementMediaURLKey = @"WebElementMediaURL"; +NSString *WebElementSpellingToolTipKey = @"WebElementSpellingToolTip"; +NSString *WebElementTitleKey = @"WebElementTitle"; +NSString *WebElementLinkIsLiveKey = @"WebElementLinkIsLive"; +NSString *WebElementIsInScrollBarKey = @"WebElementIsInScrollBar"; +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"; + +NSString *_WebViewDidStartAcceleratedCompositingNotification = @"_WebViewDidStartAcceleratedCompositing"; + +@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 +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) +static BOOL automaticQuoteSubstitutionEnabled; +static BOOL automaticLinkDetectionEnabled; +static BOOL automaticDashSubstitutionEnabled; +static BOOL automaticTextReplacementEnabled; +static BOOL automaticSpellingCorrectionEnabled; +#endif + +@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) + +static inline int callGestalt(OSType selector) +{ + SInt32 value = 0; + Gestalt(selector, &value); + return value; +} + +// Uses underscores instead of dots because if "4." ever appears in a user agent string, old DHTML libraries treat it as Netscape 4. +static NSString *createMacOSXVersionString() +{ + // Can't use -[NSProcessInfo operatingSystemVersionString] because it has too much stuff we don't want. + int major = callGestalt(gestaltSystemVersionMajor); + ASSERT(major); + + int minor = callGestalt(gestaltSystemVersionMinor); + int bugFix = callGestalt(gestaltSystemVersionBugFix); + if (bugFix) + return [[NSString alloc] initWithFormat:@"%d_%d_%d", major, minor, bugFix]; + if (minor) + return [[NSString alloc] initWithFormat:@"%d_%d", major, minor]; + return [[NSString alloc] initWithFormat:@"%d", major]; +} + +static NSString *createUserVisibleWebKitVersionString() +{ + // 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> + NSString *fullVersion = [[NSBundle bundleForClass:[WebView class]] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey]; + NSRange nonDigitRange = [fullVersion rangeOfCharacterFromSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]]; + if (nonDigitRange.location == NSNotFound && [fullVersion length] >= 4) + return [[fullVersion substringFromIndex:1] copy]; + if (nonDigitRange.location != NSNotFound && nonDigitRange.location >= 4) + return [[fullVersion substringFromIndex:1] copy]; + return [fullVersion copy]; +} + ++ (NSString *)_standardUserAgentWithApplicationName:(NSString *)applicationName +{ + // Note: Do *not* move the initialization of osVersion nor webKitVersion into the declaration. + // Garbage collection won't correctly mark the global variable in that case <rdar://problem/5733674>. + static NSString *osVersion; + static NSString *webKitVersion; + if (!osVersion) + osVersion = createMacOSXVersionString(); + if (!webKitVersion) + webKitVersion = createUserVisibleWebKitVersionString(); + NSString *language = [NSUserDefaults _webkit_preferredLanguageCode]; + if ([applicationName length]) + return [NSString stringWithFormat:@"Mozilla/5.0 (Macintosh; U; " PROCESSOR " Mac OS X %@; %@) AppleWebKit/%@ (KHTML, like Gecko) %@", osVersion, language, webKitVersion, applicationName]; + return [NSString stringWithFormat:@"Mozilla/5.0 (Macintosh; U; " PROCESSOR " Mac OS X %@; %@) AppleWebKit/%@ (KHTML, like Gecko)", osVersion, language, webKitVersion]; +} + ++ (void)_reportException:(JSValueRef)exception inContext:(JSContextRef)context +{ + if (!exception || !context) + return; + + JSLock lock(SilenceAssertionsOnly); + JSC::ExecState* execState = toJS(context); + + // Make sure the context has a DOMWindow global object, otherwise this context didn't originate from a WebView. + if (!toJSDOMWindow(execState->lexicalGlobalObject())) + return; + + reportException(execState, toJS(execState, exception)); +} + +static void WebKitInitializeApplicationCachePathIfNecessary() +{ +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + static BOOL initialized = NO; + if (initialized) + return; + + NSString *appName = [[NSBundle mainBundle] bundleIdentifier]; + if (!appName) + appName = [[NSProcessInfo processInfo] processName]; + + ASSERT(appName); + + NSString* cacheDir = [NSString _webkit_localCacheDirectoryWithBundleIdentifier:appName]; + + cacheStorage().setCacheDirectory(cacheDir); + initialized = YES; +#endif +} + +static bool runningLeopardMail() +{ +#ifdef BUILDING_ON_LEOPARD + return applicationIsAppleMail(); +#endif + return NO; +} + +static bool runningTigerMail() +{ +#ifdef BUILDING_ON_TIGER + return applicationIsAppleMail(); +#endif + return NO; +} + +static bool coreVideoHas7228836Fix() +{ +#ifdef BUILDING_ON_LEOPARD + NSBundle* coreVideoFrameworkBundle = [NSBundle bundleWithPath:@"/System/Library/Frameworks/CoreVideo.framework"]; + double version = [[coreVideoFrameworkBundle objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey] doubleValue]; + return (version >= 48); +#endif + return true; +} + +static bool shouldEnableLoadDeferring() +{ + return !applicationIsAdobeInstaller(); +} + +- (void)_dispatchPendingLoadRequests +{ + resourceLoadScheduler()->servePendingRequests(); +} + +- (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]; +} + +- (BOOL)_usesDocumentViews +{ + return _private->usesDocumentViews; +} + +static NSString *leakMailQuirksUserScriptPath() +{ + NSString *scriptPath = [[NSBundle bundleForClass:[WebView class]] pathForResource:@"MailQuirksUserScript" ofType:@"js"]; + return [[NSString alloc] initWithContentsOfFile:scriptPath]; +} + +- (void)_injectMailQuirksScript +{ + static NSString *mailQuirksScriptPath = leakMailQuirksUserScriptPath(); + core(self)->group().addUserScriptToWorld(core([WebScriptWorld world]), + mailQuirksScriptPath, KURL(), 0, 0, InjectAtDocumentEnd, InjectInAllFrames); +} + +- (void)_commonInitializationWithFrameName:(NSString *)frameName groupName:(NSString *)groupName usesDocumentViews:(BOOL)usesDocumentViews +{ + WebCoreThreadViolationCheckRoundTwo(); + +#ifndef NDEBUG + WTF::RefCountedLeakCounter::suppressMessages(webViewIsOpen); +#endif + + WebPreferences *standardPreferences = [WebPreferences standardPreferences]; + [standardPreferences willAddToWebView]; + + _private->preferences = [standardPreferences retain]; + _private->catchesDelegateExceptions = YES; + _private->mainFrameDocumentReady = NO; + _private->drawsBackground = YES; + _private->backgroundColor = [[NSColor colorWithDeviceWhite:1 alpha:1] retain]; + _private->usesDocumentViews = usesDocumentViews; + _private->includesFlattenedCompositingLayersWhenDrawingToBitmap = YES; + + WebFrameView *frameView = nil; + if (_private->usesDocumentViews) { + NSRect f = [self frame]; + frameView = [[WebFrameView alloc] initWithFrame: NSMakeRect(0,0,f.size.width,f.size.height)]; + [frameView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [self addSubview:frameView]; + [frameView release]; + } + + static bool didOneTimeInitialization = false; + if (!didOneTimeInitialization) { + WebKitInitializeLoggingChannelsIfNecessary(); + WebCore::InitializeLoggingChannelsIfNecessary(); + [WebHistoryItem initWindowWatcherIfNecessary]; +#if ENABLE(DATABASE) + WebKitInitializeDatabasesIfNecessary(); +#endif + WebKitInitializeApplicationCachePathIfNecessary(); + patchMailRemoveAttributesMethod(); + + // Initialize our platform strategies. + WebPlatformStrategies::initialize(); + Settings::setMinDOMTimerInterval(0.004); + + didOneTimeInitialization = true; + } + + Page::PageClients pageClients; + pageClients.chromeClient = new WebChromeClient(self); + pageClients.contextMenuClient = new WebContextMenuClient(self); + pageClients.editorClient = new WebEditorClient(self); + pageClients.dragClient = new WebDragClient(self); + pageClients.inspectorClient = new WebInspectorClient(self); + pageClients.pluginHalterClient = new WebPluginHalterClient(self); +#if ENABLE(CLIENT_BASED_GEOLOCATION) + pageClients.geolocationClient = new WebGeolocationClient(self); +#endif +#if ENABLE(DEVICE_ORIENTATION) + pageClients.deviceOrientationClient = new WebDeviceOrientationClient(self); +#endif + _private->page = new Page(pageClients); + + _private->page->setCanStartMedia([self window]); + _private->page->settings()->setLocalStorageDatabasePath([[self preferences] _localStorageDatabasePath]); + + [WebFrame _createMainFrameWithPage:_private->page frameName:frameName frameView:frameView]; + +#ifndef BUILDING_ON_TIGER + NSRunLoop *runLoop = [NSRunLoop mainRunLoop]; +#else + NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; +#endif + + if (WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_LOADING_DURING_COMMON_RUNLOOP_MODES)) + [self scheduleInRunLoop:runLoop forMode:(NSString *)kCFRunLoopCommonModes]; + else + [self scheduleInRunLoop:runLoop forMode:NSDefaultRunLoopMode]; + + [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 && nextKeyView != frameView) + [frameView setNextKeyView:nextKeyView]; + [super setNextKeyView:frameView]; + + if ([[self class] shouldIncludeInWebKitStatistics]) + ++WebViewCount; + + [self _registerDraggedTypes]; + + 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)) { + // Originally, we allowed all local loads. + SecurityOrigin::setLocalLoadPolicy(SecurityOrigin::AllowLocalLoadsForAll); + } else if (!WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_MORE_STRICT_LOCAL_RESOURCE_SECURITY_RESTRICTION)) { + // Later, we allowed local loads for local URLs and documents loaded + // with substitute data. + SecurityOrigin::setLocalLoadPolicy(SecurityOrigin::AllowLocalLoadsForLocalAndSubstituteData); + } + + if (!WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_CONTENT_SNIFFING_FOR_FILE_URLS)) + ResourceHandle::forceContentSniffing(); + +#if ENABLE(GLIB_SUPPORT) + [self _scheduleGlibContextIterations]; +#endif + + if (runningTigerMail() || runningLeopardMail()) + [self _injectMailQuirksScript]; +} + +- (id)_initWithFrame:(NSRect)f frameName:(NSString *)frameName groupName:(NSString *)groupName usesDocumentViews:(BOOL)usesDocumentViews +{ + 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 usesDocumentViews:usesDocumentViews]; + [self setMaintainsBackForwardList: YES]; + return self; +} + +- (BOOL)_mustDrawUnionedRect:(NSRect)rect singleRects:(const NSRect *)rects count:(NSInteger)count +{ + // If count == 0 here, use the rect passed in for drawing. This is a workaround for: + // <rdar://problem/3908282> REGRESSION (Mail): No drag image dragging selected text in Blot and Mail + // The reason for the workaround is that this method is called explicitly from the code + // to generate a drag image, and at that time, getRectsBeingDrawn:count: will return a zero count. + const int cRectThreshold = 10; + const float cWastedSpaceThreshold = 0.75f; + BOOL useUnionedRect = (count <= 1) || (count > cRectThreshold); + if (!useUnionedRect) { + // Attempt to guess whether or not we should use the unioned rect or the individual rects. + // We do this by computing the percentage of "wasted space" in the union. If that wasted space + // is too large, then we will do individual rect painting instead. + float unionPixels = (rect.size.width * rect.size.height); + float singlePixels = 0; + for (int i = 0; i < count; ++i) + singlePixels += rects[i].size.width * rects[i].size.height; + float wastedSpace = 1 - (singlePixels / unionPixels); + if (wastedSpace <= cWastedSpaceThreshold) + useUnionedRect = YES; + } + return useUnionedRect; +} + +- (void)drawSingleRect:(NSRect)rect +{ + ASSERT(!_private->usesDocumentViews); + + [NSGraphicsContext saveGraphicsState]; + NSRectClip(rect); + + @try { + [[self mainFrame] _drawRect:rect contentsOnly:NO]; + + [[self _UIDelegateForwarder] webView:self didDrawRect:rect]; + + if (WebNodeHighlight *currentHighlight = [self currentNodeHighlight]) + [currentHighlight setNeedsUpdateInTargetViewRect:rect]; + + [NSGraphicsContext restoreGraphicsState]; + } @catch (NSException *localException) { + [NSGraphicsContext restoreGraphicsState]; + LOG_ERROR("Exception caught while drawing: %@", localException); + [localException raise]; + } +} + +- (BOOL)isFlipped +{ + return _private && !_private->usesDocumentViews; +} + +- (void)setFrameSize:(NSSize)size +{ + if (!_private->usesDocumentViews && !NSEqualSizes(_private->lastLayoutSize, size)) { + Frame* frame = [self _mainCoreFrame]; + // FIXME: Viewless WebKit is broken with Safari banners (e.g., the Find banner). We'll have to figure out a way for + // Safari to communicate that this space is being consumed. For WebKit with document views, there's no + // need to do an explicit resize, since WebFrameViews have auto resizing turned on and will handle changing + // their bounds automatically. See <rdar://problem/6835573> for details. + frame->view()->resize(IntSize(size)); + frame->view()->setNeedsLayout(); + [self setNeedsDisplay:YES]; + _private->lastLayoutSize = size; + } + + [super setFrameSize:size]; +} + +#if USE(ACCELERATED_COMPOSITING) || !defined(BUILDING_ON_TIGER) + +- (void)_viewWillDrawInternal +{ + Frame* frame = [self _mainCoreFrame]; + if (frame && frame->view()) + frame->view()->updateLayoutAndStyleIfNeededRecursive(); +} + +#endif + +#ifndef BUILDING_ON_TIGER + +- (void)viewWillDraw +{ + if (!_private->usesDocumentViews) + [self _viewWillDrawInternal]; + [super viewWillDraw]; +} + +#endif + + +- (void)drawRect:(NSRect)rect +{ + if (_private->usesDocumentViews) + return [super drawRect:rect]; + + ASSERT_MAIN_THREAD(); + + const NSRect *rects; + NSInteger count; + [self getRectsBeingDrawn:&rects count:&count]; + + + if ([self _mustDrawUnionedRect:rect singleRects:rects count:count]) + [self drawSingleRect:rect]; + else + for (int i = 0; i < count; ++i) + [self drawSingleRect:rects[i]]; +} + ++ (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; +} + +static NSMutableSet *knownPluginMIMETypes() +{ + static NSMutableSet *mimeTypes = [[NSMutableSet alloc] init]; + + return mimeTypes; +} + ++ (void)_registerPluginMIMEType:(NSString *)MIMEType +{ + [WebView registerViewClass:[WebHTMLView class] representationClass:[WebHTMLRepresentation class] forMIMEType:MIMEType]; + [knownPluginMIMETypes() addObject:MIMEType]; +} + ++ (void)_unregisterPluginMIMEType:(NSString *)MIMEType +{ + [self _unregisterViewClassAndRepresentationClassForMIMEType:MIMEType]; + [knownPluginMIMETypes() removeObject:MIMEType]; +} + ++ (BOOL)_viewClass:(Class *)vClass andRepresentationClass:(Class *)rClass forMIMEType:(NSString *)MIMEType allowingPlugins:(BOOL)allowPlugins +{ + 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. + + if (allowPlugins) { + // 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) { + if (viewClass == [WebHTMLView class] && repClass == [WebHTMLRepresentation class]) { + // Special-case WebHTMLView for text types that shouldn't be shown. + if ([[WebHTMLView unsupportedTextMIMETypes] containsObject:MIMEType]) + return NO; + + // If the MIME type is a known plug-in we might not want to load it. + if (!allowPlugins && [knownPluginMIMETypes() containsObject:MIMEType]) { + BOOL isSupportedByWebKit = [[WebHTMLView supportedNonImageMIMETypes] containsObject:MIMEType] || + [[WebHTMLView supportedMIMETypes] containsObject:MIMEType]; + + // If this is a known plug-in MIME type and WebKit can't show it natively, we don't want to show it. + if (!isSupportedByWebKit) + 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 allowingPlugins:[_private->preferences arePlugInsEnabled]]) + 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 +{ + [self _setAlwaysUsesComplexTextCodePath:f]; +} + ++ (void)_setAlwaysUsesComplexTextCodePath:(BOOL)f +{ + Font::setCodePath(f ? Font::Complex : Font::Auto); +} + ++ (BOOL)canCloseAllWebViews +{ + return DOMWindow::dispatchAllPendingBeforeUnloadEvents(); +} + ++ (void)closeAllWebViews +{ + DOMWindow::dispatchAllPendingUnloadEvents(); + + // This will close the WebViews in a random order. Change this if close order is important. + // Make a new set to avoid mutating the set we are enumerating. + NSSet *webViewsToClose = [NSSet setWithSet:(NSSet *)allWebViewsSet]; + NSEnumerator *enumerator = [webViewsToClose objectEnumerator]; + while (WebView *webView = [enumerator nextObject]) + [webView close]; +} + ++ (BOOL)canShowFile:(NSString *)path +{ + return [[self class] canShowMIMEType:[WebView _MIMETypeForFile:path]]; +} + ++ (NSString *)suggestedFileExtensionForMIMEType:(NSString *)type +{ + return WKGetPreferredExtensionForMIMEType(type); +} + +- (BOOL)_isClosed +{ + return !_private || _private->closed; +} + +- (void)_closePluginDatabases +{ + pluginDatabaseClientCount--; + + // Close both sets of plug-in databases because plug-ins need an opportunity to clean up files, etc. + + // Unload the WebView local plug-in database. + if (_private->pluginDatabase) { + [_private->pluginDatabase destroyAllPluginInstanceViews]; + [_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]; +} + +- (void)_closeWithFastTeardown +{ +#ifndef NDEBUG + WTF::RefCountedLeakCounter::suppressMessages("At least one WebView was closed with fast teardown."); +#endif + + [[NSDistributedNotificationCenter defaultCenter] removeObserver:self]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [self _closePluginDatabases]; +} + +static bool fastDocumentTeardownEnabled() +{ +#ifdef NDEBUG + static bool enabled = ![[NSUserDefaults standardUserDefaults] boolForKey:WebKitEnableFullDocumentTeardownPreferenceKey]; +#else + static bool initialized = false; + static bool enabled = false; + if (!initialized) { + // This allows debug builds to default to not have fast teardown, so leak checking still works. + // But still allow the WebKitEnableFullDocumentTeardown default to override it if present. + NSNumber *setting = [[NSUserDefaults standardUserDefaults] objectForKey:WebKitEnableFullDocumentTeardownPreferenceKey]; + if (setting) + enabled = ![setting boolValue]; + initialized = true; + } +#endif + return enabled; +} + +// _close is here only for backward compatibility; clients and subclasses should use +// public method -close instead. +- (void)_close +{ + if (!_private || _private->closed) + return; + + _private->closed = YES; + [self _removeFromAllWebViewsSet]; + + [self _closingEventHandling]; + +#ifndef NDEBUG + WTF::RefCountedLeakCounter::cancelMessageSuppression(webViewIsOpen); +#endif + + // To quit the apps fast we skip document teardown, except plugins + // need to be destroyed and unloaded. + if (applicationIsTerminating && fastDocumentTeardownEnabled()) { + [self _closeWithFastTeardown]; + return; + } + +#if ENABLE(VIDEO) + [self _exitFullscreen]; +#endif + + if (Frame* mainFrame = [self _mainCoreFrame]) + mainFrame->loader()->detachFromParent(); + + [self setHostWindow:nil]; + + [self setDownloadDelegate:nil]; + [self setEditingDelegate:nil]; + [self setFrameLoadDelegate:nil]; + [self setPolicyDelegate:nil]; + [self setResourceLoadDelegate:nil]; + [self setScriptDebugDelegate:nil]; + [self setUIDelegate:nil]; + + [_private->inspector webViewClosed]; + + // 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. + Page* page = _private->page; + _private->page = 0; + delete page; + + if (_private->hasSpellCheckerDocumentTag) { + [[NSSpellChecker sharedSpellChecker] closeSpellDocumentWithTag:_private->spellCheckerDocumentTag]; + _private->hasSpellCheckerDocumentTag = NO; + } + +#if USE(ACCELERATED_COMPOSITING) + [self _clearLayerSyncLoopObserver]; +#endif + +#if ENABLE(GLIB_SUPPORT) + [self _clearGlibLoopObserver]; +#endif + + [[NSDistributedNotificationCenter defaultCenter] removeObserver:self]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [WebPreferences _removeReferenceForIdentifier:[self preferencesIdentifier]]; + + WebPreferences *preferences = _private->preferences; + _private->preferences = nil; + [preferences didRemoveFromWebView]; + [preferences release]; + + [self _closePluginDatabases]; + +#ifndef NDEBUG + // Need this to make leak messages accurate. + if (applicationIsTerminating) { + gcController().garbageCollectNow(); + [WebCache setDisabled:YES]; + } +#endif +} + +// Indicates if the WebView is in the midst of a user gesture. +- (BOOL)_isProcessingUserGesture +{ + WebFrame *frame = [self mainFrame]; + return core(frame)->loader()->isProcessingUserGesture(); +} + ++ (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 +{ + NSDictionary *features = [[NSDictionary alloc] init]; + WebView *newWindowWebView = [[self _UIDelegateForwarder] webView:self + createWebViewWithRequest:nil + windowFeatures:features]; + [features release]; + if (!newWindowWebView) + return nil; + + CallUIDelegate(newWindowWebView, @selector(webViewShow:)); + return newWindowWebView; +} + +- (WebInspector *)inspector +{ + if (!_private->inspector) + _private->inspector = [[WebInspector alloc] initWithWebView:self]; + return _private->inspector; +} + +- (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()->history()->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 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; +} + +- (BOOL)_needsLinkElementTextCSSQuirk +{ + static BOOL needsQuirk = !WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_LINK_ELEMENT_TEXT_CSS_QUIRK) + && WKAppVersionCheckLessThan(@"com.e-frontier.shade10", -1, 10.6); + return needsQuirk; +} + +- (BOOL)_needsKeyboardEventDisambiguationQuirks +{ + static BOOL needsQuirks = !WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_IE_COMPATIBLE_KEYBOARD_EVENT_DISPATCH) && !applicationIsSafari(); + return needsQuirks; +} + +- (BOOL)_needsFrameLoadDelegateRetainQuirk +{ + static BOOL needsQuirk = WKAppVersionCheckLessThan(@"com.equinux.iSale5", -1, 5.6); + return needsQuirk; +} + + +- (BOOL)_needsPreHTML5ParserQuirks +{ + // AOL Instant Messenger and Microsoft My Day contain markup incompatible + // with the new HTML5 parser. If these applications were linked against a + // version of WebKit prior to the introduction of the HTML5 parser, enable + // parser quirks to maintain compatibility. For details, see + // <https://bugs.webkit.org/show_bug.cgi?id=46134> and + // <https://bugs.webkit.org/show_bug.cgi?id=46334>. + static bool isApplicationNeedingParserQuirks = !WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_HTML5_PARSER) + && (applicationIsAOLInstantMessenger() || applicationIsMicrosoftMyDay()); + + // Mail.app must continue to display HTML email that contains quirky markup. + static bool isAppleMail = applicationIsAppleMail(); + + return isApplicationNeedingParserQuirks + || isAppleMail +#if ENABLE(DASHBOARD_SUPPORT) + // Pre-HTML5 parser quirks are required to remain compatible with many + // Dashboard widgets. See <rdar://problem/8175982>. + || (_private->page && _private->page->settings()->usesDashboardBackwardCompatibilityMode()) +#endif + || [[self preferences] usePreHTML5ParserQuirks]; +} + +- (BOOL)_needsUnrestrictedGetMatchedCSSRules +{ + static bool needsUnrestrictedGetMatchedCSSRules = !WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_GET_MATCHED_CSS_RULES_RESTRICTIONS) && applicationIsSafari(); + return needsUnrestrictedGetMatchedCSSRules; +} + +- (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->setUsesEncodingDetector([preferences usesEncodingDetector]); + settings->setFantasyFontFamily([preferences fantasyFontFamily]); + settings->setFixedFontFamily([preferences fixedFontFamily]); + settings->setForceFTPDirectoryListings([preferences _forceFTPDirectoryListings]); + settings->setFTPDirectoryTemplatePath([preferences _ftpDirectoryTemplatePath]); + settings->setLocalStorageDatabasePath([preferences _localStorageDatabasePath]); + settings->setJavaEnabled([preferences isJavaEnabled]); + settings->setJavaScriptEnabled([preferences isJavaScriptEnabled]); + settings->setWebSecurityEnabled([preferences isWebSecurityEnabled]); + settings->setAllowUniversalAccessFromFileURLs([preferences allowUniversalAccessFromFileURLs]); + settings->setAllowFileAccessFromFileURLs([preferences allowFileAccessFromFileURLs]); + settings->setJavaScriptCanOpenWindowsAutomatically([preferences javaScriptCanOpenWindowsAutomatically]); + settings->setMinimumFontSize([preferences minimumFontSize]); + settings->setMinimumLogicalFontSize([preferences minimumLogicalFontSize]); + settings->setPluginsEnabled([preferences arePlugInsEnabled]); +#if ENABLE(DATABASE) + AbstractDatabase::setIsAvailable([preferences databasesEnabled]); +#endif + settings->setLocalStorageEnabled([preferences localStorageEnabled]); + settings->setExperimentalNotificationsEnabled([preferences experimentalNotificationsEnabled]); + 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->setEditingBehaviorType(core([preferences editingBehavior])); + settings->setTextDirectionSubmenuInclusionBehavior(core([preferences textDirectionSubmenuInclusionBehavior])); + settings->setDOMPasteAllowed([preferences isDOMPasteAllowed]); + settings->setUsesPageCache([self usesPageCache]); + settings->setShowsURLsInToolTips([preferences showsURLsInToolTips]); + settings->setDeveloperExtrasEnabled([preferences developerExtrasEnabled]); + settings->setAuthorAndUserStylesEnabled([preferences authorAndUserStylesEnabled]); + settings->setApplicationChromeMode([preferences applicationChromeModeEnabled]); + if ([preferences userStyleSheetEnabled]) { + NSString* location = [[preferences userStyleSheetLocation] _web_originalDataAsString]; + if ([location isEqualToString:@"apple-dashboard://stylesheet"]) + location = @"file:///System/Library/PrivateFrameworks/DashboardClient.framework/Resources/widget.css"; + settings->setUserStyleSheetLocation([NSURL URLWithString:(location ? location : @"")]); + } else + settings->setUserStyleSheetLocation([NSURL URLWithString:@""]); + settings->setNeedsAdobeFrameReloadingQuirk([self _needsAdobeFrameReloadingQuirk]); + settings->setTreatsAnyTextCSSLinkAsStylesheet([self _needsLinkElementTextCSSQuirk]); + settings->setNeedsKeyboardEventDisambiguationQuirks([self _needsKeyboardEventDisambiguationQuirks]); + settings->setNeedsLeopardMailQuirks(runningLeopardMail()); + settings->setNeedsTigerMailQuirks(runningTigerMail()); + settings->setNeedsSiteSpecificQuirks(_private->useSiteSpecificSpoofing); + settings->setWebArchiveDebugModeEnabled([preferences webArchiveDebugModeEnabled]); + settings->setLocalFileContentSniffingEnabled([preferences localFileContentSniffingEnabled]); + settings->setOfflineWebApplicationCacheEnabled([preferences offlineWebApplicationCacheEnabled]); + settings->setJavaScriptCanAccessClipboard([preferences javaScriptCanAccessClipboard]); + settings->setXSSAuditorEnabled([preferences isXSSAuditorEnabled]); + settings->setEnforceCSSMIMETypeInNoQuirksMode(!WKAppVersionCheckLessThan(@"com.apple.iWeb", -1, 2.1)); + settings->setDNSPrefetchingEnabled([preferences isDNSPrefetchingEnabled]); + + // FIXME: Enabling accelerated compositing when WebGL is enabled causes tests to fail on Leopard which expect HW compositing to be disabled. + // Until we fix that, I will comment out the test (CFM) + settings->setAcceleratedCompositingEnabled((coreVideoHas7228836Fix() || [preferences webGLEnabled] || + [preferences accelerated2dCanvasEnabled]) && [preferences acceleratedCompositingEnabled]); + settings->setShowDebugBorders([preferences showDebugBorders]); + settings->setShowRepaintCounter([preferences showRepaintCounter]); + settings->setPluginAllowedRunTime([preferences pluginAllowedRunTime]); + settings->setWebGLEnabled([preferences webGLEnabled]); + settings->setAccelerated2dCanvasEnabled([preferences accelerated2dCanvasEnabled]); + settings->setLoadDeferringEnabled(shouldEnableLoadDeferring()); + settings->setFrameFlatteningEnabled([preferences isFrameFlatteningEnabled]); + settings->setSpatialNavigationEnabled([preferences isSpatialNavigationEnabled]); + settings->setPaginateDuringLayoutEnabled([preferences paginateDuringLayoutEnabled]); +#if ENABLE(FULLSCREEN_API) + settings->setFullScreenEnabled([preferences fullScreenEnabled]); +#endif +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + // Asynchronous spell checking API is available for 10.6 or later. + settings->setAsynchronousSpellCheckingEnabled([preferences asynchronousSpellCheckingEnabled]); +#endif + settings->setMemoryInfoEnabled([preferences memoryInfoEnabled]); + settings->setHyperlinkAuditingEnabled([preferences hyperlinkAuditingEnabled]); + settings->setUsePreHTML5ParserQuirks([self _needsPreHTML5ParserQuirks]); + settings->setCrossOriginCheckInGetMatchedCSSRulesDisabled([self _needsUnrestrictedGetMatchedCSSRules]); + + // Application Cache Preferences are stored on the global cache storage manager, not in Settings. + [WebApplicationCache setDefaultOriginQuota:[preferences applicationCacheDefaultOriginQuota]]; + + BOOL zoomsTextOnly = [preferences zoomsTextOnly]; + if (_private->zoomsTextOnly != zoomsTextOnly) + [self _setZoomMultiplier:_private->zoomMultiplier isTextOnly:zoomsTextOnly]; +} + +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; + + if (!delegate) { + bzero(cache, sizeof(WebResourceDelegateImplementationCache)); + return; + } + + 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:)); +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + cache->canAuthenticateAgainstProtectionSpaceFunc = getMethod(delegate, @selector(webView:resource:canAuthenticateAgainstProtectionSpace:forDataSource:)); +#endif + 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:)); + cache->shouldUseCredentialStorageFunc = getMethod(delegate, @selector(webView:resource:shouldUseCredentialStorageForDataSource:)); +} + +- (void)_cacheFrameLoadDelegateImplementations +{ + WebFrameLoadDelegateImplementationCache *cache = &_private->frameLoadDelegateImplementations; + id delegate = _private->frameLoadDelegate; + + if (!delegate) { + bzero(cache, sizeof(WebFrameLoadDelegateImplementationCache)); + return; + } + + cache->didCancelClientRedirectForFrameFunc = getMethod(delegate, @selector(webView:didCancelClientRedirectForFrame:)); + cache->didChangeLocationWithinPageForFrameFunc = getMethod(delegate, @selector(webView:didChangeLocationWithinPageForFrame:)); + cache->didPushStateWithinPageForFrameFunc = getMethod(delegate, @selector(webView:didPushStateWithinPageForFrame:)); + cache->didReplaceStateWithinPageForFrameFunc = getMethod(delegate, @selector(webView:didReplaceStateWithinPageForFrame:)); + cache->didPopStateWithinPageForFrameFunc = getMethod(delegate, @selector(webView:didPopStateWithinPageForFrame:)); + cache->didClearWindowObjectForFrameFunc = getMethod(delegate, @selector(webView:didClearWindowObject:forFrame:)); + cache->didClearWindowObjectForFrameInScriptWorldFunc = getMethod(delegate, @selector(webView:didClearWindowObjectForFrame:inScriptWorld:)); + cache->didClearInspectorWindowObjectForFrameFunc = getMethod(delegate, @selector(webView:didClearInspectorWindowObject: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->didFirstVisuallyNonEmptyLayoutInFrameFunc = getMethod(delegate, @selector(webView:didFirstVisuallyNonEmptyLayoutInFrame:)); + 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:)); + cache->didDisplayInsecureContentFunc = getMethod(delegate, @selector(webViewDidDisplayInsecureContent:)); + cache->didRunInsecureContentFunc = getMethod(delegate, @selector(webView:didRunInsecureContent:)); +} + +- (void)_cacheScriptDebugDelegateImplementations +{ + WebScriptDebugDelegateImplementationCache *cache = &_private->scriptDebugDelegateImplementations; + id delegate = _private->scriptDebugDelegate; + + if (!delegate) { + bzero(cache, sizeof(WebScriptDebugDelegateImplementationCache)); + return; + } + + cache->didParseSourceFunc = getMethod(delegate, @selector(webView:didParseSource:baseLineNumber:fromURL:sourceId:forWebFrame:)); + if (cache->didParseSourceFunc) + cache->didParseSourceExpectsBaseLineNumber = YES; + else + cache->didParseSourceFunc = getMethod(delegate, @selector(webView:didParseSource:fromURL:sourceId:forWebFrame:)); + + cache->failedToParseSourceFunc = getMethod(delegate, @selector(webView:failedToParseSource:baseLineNumber:fromURL:withError:forWebFrame:)); + cache->didEnterCallFrameFunc = getMethod(delegate, @selector(webView:didEnterCallFrame:sourceId:line:forWebFrame:)); + cache->willExecuteStatementFunc = getMethod(delegate, @selector(webView:willExecuteStatement:sourceId:line:forWebFrame:)); + cache->willLeaveCallFrameFunc = getMethod(delegate, @selector(webView:willLeaveCallFrame:sourceId:line:forWebFrame:)); + cache->exceptionWasRaisedFunc = getMethod(delegate, @selector(webView:exceptionWasRaised:sourceId:line:forWebFrame:)); +} + +- (void)_cacheHistoryDelegateImplementations +{ + WebHistoryDelegateImplementationCache *cache = &_private->historyDelegateImplementations; + id delegate = _private->historyDelegate; + + if (!delegate) { + bzero(cache, sizeof(WebHistoryDelegateImplementationCache)); + return; + } + + cache->navigatedFunc = getMethod(delegate, @selector(webView:didNavigateWithNavigationData:inFrame:)); + cache->clientRedirectFunc = getMethod(delegate, @selector(webView:didPerformClientRedirectFromURL:toURL:inFrame:)); + cache->serverRedirectFunc = getMethod(delegate, @selector(webView:didPerformServerRedirectFromURL:toURL:inFrame:)); + cache->setTitleFunc = getMethod(delegate, @selector(webView:updateHistoryTitle:forURL:)); + cache->populateVisitedLinksFunc = getMethod(delegate, @selector(populateVisitedLinksForWebView:)); +} + +- (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; +} + +- (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 forMainFrame:(BOOL)forMainFrame +{ + // 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]; + + // Representations for URL schemes work at the top level. + if (forMainFrame && [self _representationExistsForURLScheme:scheme]) + return YES; + + return [scheme _webkit_isCaseInsensitiveEqualToString:@"applewebdata"]; +} + ++ (BOOL)_canHandleRequest:(NSURLRequest *)request +{ + return [self _canHandleRequest:request forMainFrame:YES]; +} + ++ (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 = TextResourceDecoder::create("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]; +} + +- (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); +} + +#if ENABLE(DASHBOARD_SUPPORT) + +#define DASHBOARD_CONTROL_LABEL @"control" + +- (void)_addControlRect:(NSRect)bounds clip:(NSRect)clip fromView:(NSView *)view toDashboardRegions:(NSMutableDictionary *)regions +{ + NSRect adjustedBounds = bounds; + adjustedBounds.origin = [self convertPoint:bounds.origin fromView:view]; + adjustedBounds.origin.y = [self bounds].size.height - adjustedBounds.origin.y; + adjustedBounds.size = bounds.size; + + NSRect adjustedClip; + adjustedClip.origin = [self convertPoint:clip.origin fromView:view]; + adjustedClip.origin.y = [self bounds].size.height - adjustedClip.origin.y; + adjustedClip.size = clip.size; + + WebDashboardRegion *region = [[WebDashboardRegion alloc] initWithRect:adjustedBounds + clip:adjustedClip type:WebDashboardRegionTypeScrollerRectangle]; + NSMutableArray *scrollerRegions = [regions objectForKey:DASHBOARD_CONTROL_LABEL]; + if (!scrollerRegions) { + scrollerRegions = [[NSMutableArray alloc] init]; + [regions setObject:scrollerRegions forKey:DASHBOARD_CONTROL_LABEL]; + [scrollerRegions release]; + } + [scrollerRegions addObject:region]; + [region release]; +} + +- (void)_addScrollerDashboardRegionsForFrameView:(FrameView*)frameView dashboardRegions:(NSMutableDictionary *)regions +{ + NSView *documentView = [[kit(frameView->frame()) frameView] documentView]; + + const HashSet<RefPtr<Widget> >* children = frameView->children(); + HashSet<RefPtr<Widget> >::const_iterator end = children->end(); + for (HashSet<RefPtr<Widget> >::const_iterator it = children->begin(); it != end; ++it) { + Widget* widget = (*it).get(); + if (widget->isFrameView()) { + [self _addScrollerDashboardRegionsForFrameView:static_cast<FrameView*>(widget) dashboardRegions:regions]; + continue; + } + + if (!widget->isScrollbar()) + continue; + + // FIXME: This should really pass an appropriate clip, but our first try got it wrong, and + // it's not common to need this to be correct in Dashboard widgets. + NSRect bounds = widget->frameRect(); + [self _addControlRect:bounds clip:bounds fromView:documentView toDashboardRegions:regions]; + } +} + +- (void)_addScrollerDashboardRegions:(NSMutableDictionary *)regions from:(NSArray *)views +{ + // Add scroller regions for NSScroller and WebCore scrollbars + NSUInteger count = [views count]; + for (NSUInteger i = 0; i < count; i++) { + NSView *view = [views objectAtIndex:i]; + + if ([view isKindOfClass:[WebHTMLView class]]) { + if (Frame* coreFrame = core([(WebHTMLView*)view _frame])) { + if (FrameView* coreView = coreFrame->view()) + [self _addScrollerDashboardRegionsForFrameView:coreView dashboardRegions:regions]; + } + } else if ([view isKindOfClass:[NSScroller class]]) { + // AppKit places absent scrollers at -100,-100 + if ([view frame].origin.y < 0) + continue; + [self _addControlRect:[view bounds] clip:[view visibleRect] fromView:view toDashboardRegions:regions]; + } + [self _addScrollerDashboardRegions:regions from:[view subviews]]; + } +} + +- (void)_addScrollerDashboardRegions:(NSMutableDictionary *)regions +{ + [self _addScrollerDashboardRegions:regions from:[self subviews]]; +} + +- (NSDictionary *)_dashboardRegions +{ + // Only return regions from main frame. + Frame* mainFrame = [self _mainCoreFrame]; + if (!mainFrame) + return nil; + + const Vector<DashboardRegionValue>& regions = mainFrame->document()->dashboardRegions(); + size_t size = regions.size(); + + NSMutableDictionary *webRegions = [NSMutableDictionary dictionaryWithCapacity:size]; + for (size_t i = 0; i < size; i++) { + const DashboardRegionValue& region = regions[i]; + + if (region.type == StyleDashboardRegion::None) + continue; + + NSString *label = region.label; + WebDashboardRegionType type = WebDashboardRegionTypeNone; + if (region.type == StyleDashboardRegion::Circle) + type = WebDashboardRegionTypeCircle; + else if (region.type == StyleDashboardRegion::Rectangle) + type = WebDashboardRegionTypeRectangle; + NSMutableArray *regionValues = [webRegions objectForKey:label]; + if (!regionValues) { + regionValues = [[NSMutableArray alloc] initWithCapacity:1]; + [webRegions setObject:regionValues forKey:label]; + [regionValues release]; + } + + WebDashboardRegion *webRegion = [[WebDashboardRegion alloc] initWithRect:region.bounds clip:region.clip type:type]; + [regionValues addObject:webRegion]; + [webRegion release]; + } + + [self _addScrollerDashboardRegions:webRegions]; + + return webRegions; +} + +- (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; + } + } + + // Pre-HTML5 parser quirks should be enabled if Dashboard is in backward + // compatibility mode. See <rdar://problem/8175982>. + if (_private->page) + _private->page->settings()->setUsePreHTML5ParserQuirks([self _needsPreHTML5ParserQuirks]); +} + +- (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; +} + +#endif /* ENABLE(DASHBOARD_SUPPORT) */ + ++ (void)_setShouldUseFontSmoothing:(BOOL)f +{ + Font::setShouldUseSmoothing(f); +} + ++ (BOOL)_shouldUseFontSmoothing +{ + return Font::shouldUseSmoothing(); +} + ++ (void)_setUsesTestModeFocusRingColor:(BOOL)f +{ + setUsesTestModeFocusRingColor(f); +} + ++ (BOOL)_usesTestModeFocusRingColor +{ + return usesTestModeFocusRingColor(); +} + +- (void)setAlwaysShowVerticalScroller:(BOOL)flag +{ + WebDynamicScrollBarsView *scrollview = [[[self mainFrame] frameView] _scrollView]; + if (flag) { + [scrollview setVerticalScrollingMode:ScrollbarAlwaysOn andLock:YES]; + } else { + [scrollview setVerticalScrollingModeLocked:NO]; + [scrollview setVerticalScrollingMode:ScrollbarAuto andLock:NO]; + } +} + +- (BOOL)alwaysShowVerticalScroller +{ + WebDynamicScrollBarsView *scrollview = [[[self mainFrame] frameView] _scrollView]; + return [scrollview verticalScrollingModeLocked] && [scrollview verticalScrollingMode] == ScrollbarAlwaysOn; +} + +- (void)setAlwaysShowHorizontalScroller:(BOOL)flag +{ + WebDynamicScrollBarsView *scrollview = [[[self mainFrame] frameView] _scrollView]; + if (flag) { + [scrollview setHorizontalScrollingMode:ScrollbarAlwaysOn andLock:YES]; + } else { + [scrollview setHorizontalScrollingModeLocked:NO]; + [scrollview setHorizontalScrollingMode:ScrollbarAuto andLock:NO]; + } +} + +- (void)setProhibitsMainFrameScrolling:(BOOL)prohibits +{ + if (Frame* mainFrame = [self _mainCoreFrame]) + mainFrame->view()->setProhibitsScrolling(prohibits); +} + +- (BOOL)alwaysShowHorizontalScroller +{ + WebDynamicScrollBarsView *scrollview = [[[self mainFrame] frameView] _scrollView]; + return [scrollview horizontalScrollingModeLocked] && [scrollview horizontalScrollingMode] == ScrollbarAlwaysOn; +} + +- (void)_setInViewSourceMode:(BOOL)flag +{ + if (Frame* mainFrame = [self _mainCoreFrame]) + mainFrame->setInViewSourceMode(flag); +} + +- (BOOL)_inViewSourceMode +{ + Frame* mainFrame = [self _mainCoreFrame]; + 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; +} + +- (BOOL)_cookieEnabled +{ + if (_private->page) + return _private->page->cookieEnabled(); + return YES; +} + +- (void)_setCookieEnabled:(BOOL)enable +{ + if (_private->page) + _private->page->setCookieEnabled(enable); +} + +- (void)_setAdditionalWebPlugInPaths:(NSArray *)newPaths +{ + if (!_private->pluginDatabase) + _private->pluginDatabase = [[WebPluginDatabase alloc] init]; + + [_private->pluginDatabase setPlugInPaths:newPaths]; + [_private->pluginDatabase refresh]; +} + +- (void)_attachScriptDebuggerToAllFrames +{ + for (Frame* frame = [self _mainCoreFrame]; frame; frame = frame->tree()->traverseNext()) + [kit(frame) _attachScriptDebugger]; +} + +- (void)_detachScriptDebuggerFromAllFrames +{ + for (Frame* frame = [self _mainCoreFrame]; 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] _updateBackgroundAndUpdatesWhileOffscreen]; +} + +- (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]; +} + +- (WebHistoryItem *)_globalHistoryItem +{ + if (!_private->page) + return nil; + return kit(_private->page->globalHistoryItem()); +} + +- (WebTextIterator *)textIteratorForRect:(NSRect)rect +{ + IntPoint rectStart(rect.origin.x, rect.origin.y); + IntPoint rectEnd(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height); + + Frame* coreFrame = [self _mainCoreFrame]; + if (!coreFrame) + return nil; + + VisibleSelection selectionInsideRect(coreFrame->visiblePositionForPoint(rectStart), coreFrame->visiblePositionForPoint(rectEnd)); + + return [[[WebTextIterator alloc] initWithRange:kit(selectionInsideRect.toNormalizedRange().get())] autorelease]; +} + +- (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; +} + +- (void)_executeCoreCommandByName:(NSString *)name value:(NSString *)value +{ + Frame* coreFrame = [self _mainCoreFrame]; + if (!coreFrame) + return; + coreFrame->editor()->command(name).execute(value); +} + +- (void)_setCustomHTMLTokenizerTimeDelay:(double)timeDelay +{ + if (!_private->page) + return; + return _private->page->setCustomHTMLTokenizerTimeDelay(timeDelay); +} + +- (void)_setCustomHTMLTokenizerChunkSize:(int)chunkSize +{ + if (!_private->page) + return; + return _private->page->setCustomHTMLTokenizerChunkSize(chunkSize); +} + +- (void)_clearMainFrameName +{ + _private->page->mainFrame()->tree()->clearName(); +} + +- (void)setSelectTrailingWhitespaceEnabled:(BOOL)flag +{ + _private->selectTrailingWhitespaceEnabled = flag; + if (flag) + [self setSmartInsertDeleteEnabled:false]; +} + +- (BOOL)isSelectTrailingWhitespaceEnabled +{ + return _private->selectTrailingWhitespaceEnabled; +} + +- (void)setMemoryCacheDelegateCallsEnabled:(BOOL)enabled +{ + _private->page->setMemoryCacheClientCallsEnabled(enabled); +} + +- (BOOL)areMemoryCacheDelegateCallsEnabled +{ + return _private->page->areMemoryCacheClientCallsEnabled(); +} + +- (void)_setJavaScriptURLsAreAllowed:(BOOL)areAllowed +{ + _private->page->setJavaScriptURLsAreAllowed(areAllowed); +} + ++ (NSCursor *)_pointingHandCursor +{ + return handCursor().platformCursor(); +} + +- (BOOL)_postsAcceleratedCompositingNotifications +{ +#if USE(ACCELERATED_COMPOSITING) + return _private->postsAcceleratedCompositingNotifications; +#else + return NO; +#endif + +} +- (void)_setPostsAcceleratedCompositingNotifications:(BOOL)flag +{ +#if USE(ACCELERATED_COMPOSITING) + _private->postsAcceleratedCompositingNotifications = flag; +#endif +} + +- (BOOL)_isUsingAcceleratedCompositing +{ +#if USE(ACCELERATED_COMPOSITING) + if (_private->usesDocumentViews) { + Frame* coreFrame = [self _mainCoreFrame]; + for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) { + NSView *documentView = [[kit(frame) frameView] documentView]; + if ([documentView isKindOfClass:[WebHTMLView class]] && [(WebHTMLView *)documentView _isUsingAcceleratedCompositing]) + return YES; + } + } +#endif + return NO; +} + +- (BOOL)_isSoftwareRenderable +{ +#if USE(ACCELERATED_COMPOSITING) + if (_private->usesDocumentViews) { + Frame* coreFrame = [self _mainCoreFrame]; + for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) { + if (FrameView* view = frame->view()) { + if (!view->isSoftwareRenderable()) + return NO; + } + } + } +#endif + return YES; +} + +- (void)_setIncludesFlattenedCompositingLayersWhenDrawingToBitmap:(BOOL)flag +{ + _private->includesFlattenedCompositingLayersWhenDrawingToBitmap = flag; +} + +- (BOOL)_includesFlattenedCompositingLayersWhenDrawingToBitmap +{ + return _private->includesFlattenedCompositingLayersWhenDrawingToBitmap; +} + +#if ENABLE(NETSCAPE_PLUGIN_API) +static WebBaseNetscapePluginView *_pluginViewForNode(DOMNode *node) +{ + if (!node) + return nil; + + Node* coreNode = core(node); + if (!coreNode) + return nil; + + RenderObject* renderer = coreNode->renderer(); + if (!renderer || !renderer->isWidget()) + return nil; + + Widget* widget = toRenderWidget(renderer)->widget(); + if (!widget || !widget->platformWidget()) + return nil; + + NSView *view = widget->platformWidget(); + if (![view isKindOfClass:[WebBaseNetscapePluginView class]]) + return nil; + + return (WebBaseNetscapePluginView *)view; +} +#endif // ENABLE(NETSCAPE_PLUGIN_API) + ++ (BOOL)_isNodeHaltedPlugin:(DOMNode *)node +{ +#if ENABLE(NETSCAPE_PLUGIN_API) + return [_pluginViewForNode(node) isHalted]; +#else + return YES; +#endif +} + ++ (BOOL)_hasPluginForNodeBeenHalted:(DOMNode *)node +{ +#if ENABLE(NETSCAPE_PLUGIN_API) + return [_pluginViewForNode(node) hasBeenHalted]; +#else + return YES; +#endif +} ++ (void)_restartHaltedPluginForNode:(DOMNode *)node +{ +#if ENABLE(NETSCAPE_PLUGIN_API) + if (!node) + return; + + [_pluginViewForNode(node) resumeFromHalt]; +#endif +} + +- (NSPasteboard *)_insertionPasteboard +{ + return _private ? _private->insertionPasteboard : nil; +} + ++ (void)_addOriginAccessWhitelistEntryWithSourceOrigin:(NSString *)sourceOrigin destinationProtocol:(NSString *)destinationProtocol destinationHost:(NSString *)destinationHost allowDestinationSubdomains:(BOOL)allowDestinationSubdomains +{ + SecurityOrigin::addOriginAccessWhitelistEntry(*SecurityOrigin::createFromString(sourceOrigin), destinationProtocol, destinationHost, allowDestinationSubdomains); +} + ++ (void)_removeOriginAccessWhitelistEntryWithSourceOrigin:(NSString *)sourceOrigin destinationProtocol:(NSString *)destinationProtocol destinationHost:(NSString *)destinationHost allowDestinationSubdomains:(BOOL)allowDestinationSubdomains +{ + SecurityOrigin::removeOriginAccessWhitelistEntry(*SecurityOrigin::createFromString(sourceOrigin), destinationProtocol, destinationHost, allowDestinationSubdomains); +} + ++(void)_resetOriginAccessWhitelists +{ + SecurityOrigin::resetOriginAccessWhitelists(); +} + +- (void)_updateActiveState +{ + if (_private && _private->page) + _private->page->focusController()->setActive([[self window] isKeyWindow]); +} + +static PassOwnPtr<Vector<String> > toStringVector(NSArray* patterns) +{ + // Convert the patterns into Vectors. + NSUInteger count = [patterns count]; + if (count == 0) + return 0; + Vector<String>* patternsVector = new Vector<String>; + for (NSUInteger i = 0; i < count; ++i) { + id entry = [patterns objectAtIndex:i]; + if ([entry isKindOfClass:[NSString class]]) + patternsVector->append(String((NSString*)entry)); + } + return patternsVector; +} + ++ (void)_addUserScriptToGroup:(NSString *)groupName world:(WebScriptWorld *)world source:(NSString *)source url:(NSURL *)url + whitelist:(NSArray *)whitelist blacklist:(NSArray *)blacklist + injectionTime:(WebUserScriptInjectionTime)injectionTime +{ + [WebView _addUserScriptToGroup:groupName world:world source:source url:url whitelist:whitelist blacklist:blacklist injectionTime:injectionTime injectedFrames:WebInjectInAllFrames]; +} + ++ (void)_addUserScriptToGroup:(NSString *)groupName world:(WebScriptWorld *)world source:(NSString *)source url:(NSURL *)url + whitelist:(NSArray *)whitelist blacklist:(NSArray *)blacklist + injectionTime:(WebUserScriptInjectionTime)injectionTime + injectedFrames:(WebUserContentInjectedFrames)injectedFrames +{ + String group(groupName); + if (group.isEmpty()) + return; + + PageGroup* pageGroup = PageGroup::pageGroup(group); + if (!pageGroup) + return; + + pageGroup->addUserScriptToWorld(core(world), source, url, toStringVector(whitelist), toStringVector(blacklist), + injectionTime == WebInjectAtDocumentStart ? InjectAtDocumentStart : InjectAtDocumentEnd, + injectedFrames == WebInjectInAllFrames ? InjectInAllFrames : InjectInTopFrameOnly); +} + ++ (void)_addUserStyleSheetToGroup:(NSString *)groupName world:(WebScriptWorld *)world source:(NSString *)source url:(NSURL *)url + whitelist:(NSArray *)whitelist blacklist:(NSArray *)blacklist +{ + [WebView _addUserStyleSheetToGroup:groupName world:world source:source url:url whitelist:whitelist blacklist:blacklist injectedFrames:WebInjectInAllFrames]; +} + ++ (void)_addUserStyleSheetToGroup:(NSString *)groupName world:(WebScriptWorld *)world source:(NSString *)source url:(NSURL *)url + whitelist:(NSArray *)whitelist blacklist:(NSArray *)blacklist + injectedFrames:(WebUserContentInjectedFrames)injectedFrames +{ + String group(groupName); + if (group.isEmpty()) + return; + + PageGroup* pageGroup = PageGroup::pageGroup(group); + if (!pageGroup) + return; + + pageGroup->addUserStyleSheetToWorld(core(world), source, url, toStringVector(whitelist), toStringVector(blacklist), injectedFrames == WebInjectInAllFrames ? InjectInAllFrames : InjectInTopFrameOnly); +} + ++ (void)_removeUserScriptFromGroup:(NSString *)groupName world:(WebScriptWorld *)world url:(NSURL *)url +{ + String group(groupName); + if (group.isEmpty()) + return; + + PageGroup* pageGroup = PageGroup::pageGroup(group); + if (!pageGroup) + return; + + pageGroup->removeUserScriptFromWorld(core(world), url); +} + ++ (void)_removeUserStyleSheetFromGroup:(NSString *)groupName world:(WebScriptWorld *)world url:(NSURL *)url +{ + String group(groupName); + if (group.isEmpty()) + return; + + PageGroup* pageGroup = PageGroup::pageGroup(group); + if (!pageGroup) + return; + + pageGroup->removeUserStyleSheetFromWorld(core(world), url); +} + ++ (void)_removeUserScriptsFromGroup:(NSString *)groupName world:(WebScriptWorld *)world +{ + String group(groupName); + if (group.isEmpty()) + return; + + PageGroup* pageGroup = PageGroup::pageGroup(group); + if (!pageGroup) + return; + + pageGroup->removeUserScriptsFromWorld(core(world)); +} + ++ (void)_removeUserStyleSheetsFromGroup:(NSString *)groupName world:(WebScriptWorld *)world +{ + String group(groupName); + if (group.isEmpty()) + return; + + PageGroup* pageGroup = PageGroup::pageGroup(group); + if (!pageGroup) + return; + + pageGroup->removeUserStyleSheetsFromWorld(core(world)); +} + ++ (void)_removeAllUserContentFromGroup:(NSString *)groupName +{ + String group(groupName); + if (group.isEmpty()) + return; + + PageGroup* pageGroup = PageGroup::pageGroup(group); + if (!pageGroup) + return; + + pageGroup->removeAllUserContent(); +} + +- (BOOL)cssAnimationsSuspended +{ + return _private->cssAnimationsSuspended; +} + +- (void)setCSSAnimationsSuspended:(BOOL)suspended +{ + if (suspended == _private->cssAnimationsSuspended) + return; + + _private->cssAnimationsSuspended = suspended; + + Frame* frame = core([self mainFrame]); + if (suspended) + frame->animation()->suspendAnimations(); + else + frame->animation()->resumeAnimations(); +} + ++ (void)_setDomainRelaxationForbidden:(BOOL)forbidden forURLScheme:(NSString *)scheme +{ + SecurityOrigin::setDomainRelaxationForbiddenForURLScheme(forbidden, scheme); +} + ++ (void)_registerURLSchemeAsSecure:(NSString *)scheme +{ + SchemeRegistry::registerURLSchemeAsSecure(scheme); +} + +- (void)_scaleWebView:(float)scale atOrigin:(NSPoint)origin +{ + Frame* coreFrame = [self _mainCoreFrame]; + if (!coreFrame) + return; + + coreFrame->scalePage(scale, IntPoint(origin)); +} + +- (float)_viewScaleFactor +{ + Frame* coreFrame = [self _mainCoreFrame]; + if (!coreFrame) + return 1; + + return coreFrame->pageScaleFactor(); +} + +- (void)_setUseFixedLayout:(BOOL)fixed +{ + Frame* coreFrame = [self _mainCoreFrame]; + if (!coreFrame) + return; + + FrameView* view = coreFrame->view(); + if (!view) + return; + + view->setUseFixedLayout(fixed); + if (!fixed) + view->setFixedLayoutSize(IntSize()); +} + +- (void)_setFixedLayoutSize:(NSSize)size +{ + Frame* coreFrame = [self _mainCoreFrame]; + if (!coreFrame) + return; + + FrameView* view = coreFrame->view(); + if (!view) + return; + + view->setFixedLayoutSize(IntSize(size)); + view->forceLayout(); +} + +- (BOOL)_useFixedLayout +{ + Frame* coreFrame = [self _mainCoreFrame]; + if (!coreFrame) + return NO; + + FrameView* view = coreFrame->view(); + if (!view) + return NO; + + return view->useFixedLayout(); +} + +- (NSSize)_fixedLayoutSize +{ + Frame* coreFrame = [self _mainCoreFrame]; + if (!coreFrame) + return IntSize(); + + FrameView* view = coreFrame->view(); + if (!view) + return IntSize(); + + return view->fixedLayoutSize(); +} + +- (NSUInteger)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag highlight:(BOOL)highlight limit:(NSUInteger)limit +{ + return [self countMatchesForText:string options:(caseFlag ? 0 : WebFindOptionsCaseInsensitive) highlight:highlight limit:limit markMatches:YES]; +} + +- (NSUInteger)countMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag highlight:(BOOL)highlight limit:(NSUInteger)limit markMatches:(BOOL)markMatches +{ + return [self countMatchesForText:string options:(caseFlag ? 0 : WebFindOptionsCaseInsensitive) highlight:highlight limit:limit markMatches:markMatches]; +} + +- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection +{ + return [self findString:string options:((forward ? 0 : WebFindOptionsBackwards) | (caseFlag ? 0 : WebFindOptionsCaseInsensitive) | (wrapFlag ? WebFindOptionsWrapAround : 0) | (startInSelection ? WebFindOptionsStartInSelection : 0))]; +} + ++ (void)_setLoadResourcesSerially:(BOOL)serialize +{ + resourceLoadScheduler()->setSerialLoadingEnabled(serialize); +} + +@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; + + InitWebCoreSystemInterface(); + JSC::initializeThreading(); + WTF::initializeMainThreadToProcessMainThread(); + + [[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]; + + continuousSpellCheckingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebContinuousSpellCheckingEnabled]; +#ifndef BUILDING_ON_TIGER + grammarCheckingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebGrammarCheckingEnabled]; +#endif + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + automaticQuoteSubstitutionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebAutomaticQuoteSubstitutionEnabled]; + automaticLinkDetectionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebAutomaticLinkDetectionEnabled]; + automaticDashSubstitutionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebAutomaticDashSubstitutionEnabled]; + automaticTextReplacementEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebAutomaticTextReplacementEnabled]; + automaticSpellingCorrectionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebAutomaticSpellingCorrectionEnabled]; +#endif + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) + if (![[NSUserDefaults standardUserDefaults] objectForKey:WebAutomaticTextReplacementEnabled]) + automaticTextReplacementEnabled = [NSSpellChecker isAutomaticTextReplacementEnabled]; + if (![[NSUserDefaults standardUserDefaults] objectForKey:WebAutomaticSpellingCorrectionEnabled]) + automaticSpellingCorrectionEnabled = [NSSpellChecker isAutomaticSpellingCorrectionEnabled]; +#endif +} + ++ (void)_applicationWillTerminate +{ + applicationIsTerminating = YES; + + if (fastDocumentTeardownEnabled()) + [self closeAllWebViews]; + + if (!pluginDatabaseClientCount) + [WebPluginDatabase closeSharedDatabase]; + + PageGroup::closeLocalStorage(); +} + ++ (BOOL)_canShowMIMEType:(NSString *)MIMEType allowingPlugins:(BOOL)allowPlugins +{ + return [self _viewClass:nil andRepresentationClass:nil forMIMEType:MIMEType allowingPlugins:allowPlugins]; +} + ++ (BOOL)canShowMIMEType:(NSString *)MIMEType +{ + return [self _canShowMIMEType:MIMEType allowingPlugins:YES]; +} + +- (BOOL)_canShowMIMEType:(NSString *)MIMEType +{ + return [[self class] _canShowMIMEType:MIMEType allowingPlugins:[_private->preferences arePlugInsEnabled]]; +} + +- (WebBasePluginPackage *)_pluginForMIMEType:(NSString *)MIMEType +{ + if (![_private->preferences arePlugInsEnabled]) + return nil; + + WebBasePluginPackage *pluginPackage = [[WebPluginDatabase sharedDatabase] pluginForMIMEType:MIMEType]; + if (pluginPackage) + return pluginPackage; + + if (_private->pluginDatabase) + return [_private->pluginDatabase pluginForMIMEType:MIMEType]; + + return nil; +} + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +- (WebBasePluginPackage *)_videoProxyPluginForMIMEType:(NSString *)MIMEType +{ + WebBasePluginPackage *pluginPackage = [[WebPluginDatabase sharedDatabase] pluginForMIMEType:MIMEType]; + if (pluginPackage) + return pluginPackage; + + if (_private->pluginDatabase) + return [_private->pluginDatabase pluginForMIMEType:MIMEType]; + + return nil; +} +#endif + +- (WebBasePluginPackage *)_pluginForExtension:(NSString *)extension +{ + if (![_private->preferences arePlugInsEnabled]) + return nil; + + WebBasePluginPackage *pluginPackage = [[WebPluginDatabase sharedDatabase] pluginForExtension:extension]; + if (pluginPackage) + return pluginPackage; + + if (_private->pluginDatabase) + return [_private->pluginDatabase pluginForExtension:extension]; + + return nil; +} + +- (void)addPluginInstanceView:(NSView *)view +{ + if (!_private->pluginDatabase) + _private->pluginDatabase = [[WebPluginDatabase alloc] init]; + [_private->pluginDatabase addPluginInstanceView:view]; +} + +- (void)removePluginInstanceView:(NSView *)view +{ + if (_private->pluginDatabase) + [_private->pluginDatabase removePluginInstanceView:view]; +} + +- (void)removePluginInstanceViewsFor:(WebFrame*)webFrame +{ + if (_private->pluginDatabase) + [_private->pluginDatabase removePluginInstanceViewsFor:webFrame]; +} + +- (BOOL)_isMIMETypeRegisteredAsPlugin:(NSString *)MIMEType +{ + if (![_private->preferences arePlugInsEnabled]) + return NO; + + 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 +{ + SchemeRegistry::registerURLSchemeAsLocal(protocol); +} + +- (id)_initWithArguments:(NSDictionary *) arguments +{ + NSCoder *decoder = [arguments objectForKey:@"decoder"]; + if (decoder) { + self = [self initWithCoder:decoder]; + } else { + ASSERT([arguments objectForKey:@"frame"]); + NSValue *frameValue = [arguments objectForKey:@"frame"]; + NSRect frame = (frameValue ? [frameValue rectValue] : NSZeroRect); + NSString *frameName = [arguments objectForKey:@"frameName"]; + NSString *groupName = [arguments objectForKey:@"groupName"]; + self = [self initWithFrame:frame frameName:frameName groupName:groupName]; + } + + return self; +} + +static bool clientNeedsWebViewInitThreadWorkaround() +{ + if (WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_WEBVIEW_INIT_THREAD_WORKAROUND)) + return false; + + NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; + + // Installer. + if ([bundleIdentifier _webkit_isCaseInsensitiveEqualToString:@"com.apple.installer"]) + return true; + + // Automator. + if ([bundleIdentifier _webkit_isCaseInsensitiveEqualToString:@"com.apple.Automator"]) + return true; + + // Automator Runner. + if ([bundleIdentifier _webkit_isCaseInsensitiveEqualToString:@"com.apple.AutomatorRunner"]) + return true; + + // Automator workflows. + if ([bundleIdentifier _webkit_hasCaseInsensitivePrefix:@"com.apple.Automator."]) + return true; + +#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) + // Mail. + if ([bundleIdentifier _webkit_isCaseInsensitiveEqualToString:@"com.apple.Mail"]) + return true; +#endif + + return false; +} + +static bool needsWebViewInitThreadWorkaround() +{ + static bool isOldClient = clientNeedsWebViewInitThreadWorkaround(); + return isOldClient && !pthread_main_np(); +} + +- (id)initWithFrame:(NSRect)f +{ + return [self initWithFrame:f frameName:nil groupName:nil]; +} + +- (id)initWithFrame:(NSRect)f frameName:(NSString *)frameName groupName:(NSString *)groupName +{ + if (needsWebViewInitThreadWorkaround()) + return [[self _webkit_invokeOnMainThread] initWithFrame:f frameName:frameName groupName:groupName]; + + WebCoreThreadViolationCheckRoundTwo(); + return [self _initWithFrame:f frameName:frameName groupName:groupName usesDocumentViews:YES]; +} + +- (id)initWithCoder:(NSCoder *)decoder +{ + if (needsWebViewInitThreadWorkaround()) + return [[self _webkit_invokeOnMainThread] initWithCoder:decoder]; + + WebCoreThreadViolationCheckRoundTwo(); + WebView *result = nil; + + @try { + 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 usesDocumentViews:YES]; + static_cast<BackForwardListImpl*>([result page]->backForwardList())->setEnabled(useBackForwardList); + result->_private->allowsUndo = allowsUndo; + if (preferences) + [result setPreferences:preferences]; + } @catch (NSException *localException) { + result = nil; + [self release]; + } + + 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 && static_cast<BackForwardListImpl*>(_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 +{ + if (WebCoreObjCScheduleDeallocateOnMainThread([WebView class], self)) + return; + + // call close to ensure we tear-down completely + // this maintains our old behavior for existing applications + [self close]; + + if ([[self class] shouldIncludeInWebKitStatistics]) + --WebViewCount; + + if ([self _needsFrameLoadDelegateRetainQuirk]) + [_private->frameLoadDelegate release]; + + [_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 +{ + // _close existed first, and some clients might be calling or overriding it, so call through. + [self _close]; +} + +- (void)setShouldCloseWithWindow:(BOOL)close +{ + _private->shouldCloseWithWindow = close; +} + +- (BOOL)shouldCloseWithWindow +{ + return _private->shouldCloseWithWindow; +} + +- (void)addWindowObserversForWindow:(NSWindow *)window +{ + if (window) { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidBecomeKey:) + name:NSWindowDidBecomeKeyNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowDidResignKey:) + name:NSWindowDidResignKeyNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_windowWillOrderOnScreen:) + name:WKWindowWillOrderOnScreenNotification() object:window]; + } +} + +- (void)removeWindowObservers +{ + NSWindow *window = [self window]; + if (window) { + [[NSNotificationCenter defaultCenter] removeObserver:self + name:NSWindowDidBecomeKeyNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self + name:NSWindowDidResignKeyNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self + name:WKWindowWillOrderOnScreenNotification() object:window]; + } +} + +- (void)viewWillMoveToWindow:(NSWindow *)window +{ + // Don't do anything if the WebView isn't initialized. + // This happens when decoding a WebView in a nib. + // FIXME: What sets up the observer of NSWindowWillCloseNotification in this case? + if (!_private || _private->closed) + return; + + if ([self window] && [self window] != [self hostWindow]) + [[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. + // The following are expensive enough that we don't want to call them over + // and over, so do them when we move into a window. + [window setAcceptsMouseMovedEvents:YES]; + WKSetNSWindowShouldPostEventNotifications(window, YES); + } else { + _private->page->setCanStartMedia(false); + _private->page->willMoveOffscreen(); + } + + if (window != [self window]) { + [self removeWindowObservers]; + [self addWindowObserversForWindow:window]; + } +} + +- (void)viewDidMoveToWindow +{ + // Don't do anything if we aren't initialized. This happens + // when decoding a WebView. When WebViews are decoded their subviews + // are created by initWithCoder: and so won't be normally + // initialized. The stub views are discarded by WebView. + if (!_private || _private->closed) + return; + + if ([self window]) { + _private->page->setCanStartMedia(true); + _private->page->didMoveOnscreen(); + } + + [self _updateActiveState]; +} + +- (void)_windowDidBecomeKey:(NSNotification *)notification +{ + NSWindow *keyWindow = [notification object]; + if (keyWindow == [self window] || keyWindow == [[self window] attachedSheet]) + [self _updateActiveState]; +} + +- (void)_windowDidResignKey:(NSNotification *)notification +{ + NSWindow *formerKeyWindow = [notification object]; + if (formerKeyWindow == [self window] || formerKeyWindow == [[self window] attachedSheet]) + [self _updateActiveState]; +} + +- (void)_windowWillOrderOnScreen:(NSNotification *)notification +{ + if (![self shouldUpdateWhileOffscreen]) + [self setNeedsDisplay: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; +} + +- (id)UIDelegate +{ + return _private->UIDelegate; +} + +- (void)setResourceLoadDelegate: delegate +{ + _private->resourceProgressDelegate = delegate; + [self _cacheResourceLoadDelegateImplementations]; +} + +- (id)resourceLoadDelegate +{ + return _private->resourceProgressDelegate; +} + +- (void)setDownloadDelegate: delegate +{ + _private->downloadDelegate = delegate; +} + + +- (id)downloadDelegate +{ + return _private->downloadDelegate; +} + +- (void)setPolicyDelegate:delegate +{ + _private->policyDelegate = delegate; + [_private->policyDelegateForwarder release]; + _private->policyDelegateForwarder = nil; +} + +- (id)policyDelegate +{ + return _private->policyDelegate; +} + +- (void)setFrameLoadDelegate:delegate +{ + // <rdar://problem/6950660> - Due to some subtle WebKit changes - presumably to delegate callback behavior - we've + // unconvered a latent bug in at least one WebKit app where the delegate wasn't properly retained by the app and + // was dealloc'ed before being cleared. + // This is an effort to keep such apps working for now. + if ([self _needsFrameLoadDelegateRetainQuirk]) { + [delegate retain]; + [_private->frameLoadDelegate release]; + } + + _private->frameLoadDelegate = delegate; + [self _cacheFrameLoadDelegateImplementations]; + +#if ENABLE(ICONDATABASE) + // If this delegate wants callbacks for icons, fire up the icon database. + if (_private->frameLoadDelegateImplementations.didReceiveIconForFrameFunc) + [WebIconDatabase sharedIconDatabase]; +#endif +} + +- (id)frameLoadDelegate +{ + return _private->frameLoadDelegate; +} + +- (WebFrame *)mainFrame +{ + // This can be called in initialization, before _private has been set up (3465613) + if (!_private || !_private->page) + return nil; + return kit(_private->page->mainFrame()); +} + +- (WebFrame *)selectedFrame +{ + if (_private->usesDocumentViews) { + // 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; + BackForwardListImpl* list = static_cast<BackForwardListImpl*>(_private->page->backForwardList()); + if (!list->enabled()) + return nil; + return kit(list); +} + +- (void)setMaintainsBackForwardList:(BOOL)flag +{ + if (!_private->page) + return; + static_cast<BackForwardListImpl*>(_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 +{ + [self _setZoomMultiplier:m isTextOnly:![[NSUserDefaults standardUserDefaults] boolForKey:WebKitDebugFullPageZoomPreferenceKey]]; +} + +- (float)textSizeMultiplier +{ + return [self _realZoomMultiplierIsTextOnly] ? _private->zoomMultiplier : 1.0f; +} + +- (void)_setZoomMultiplier:(float)multiplier isTextOnly:(BOOL)isTextOnly +{ + // NOTE: This has no visible effect when viewing a PDF (see <rdar://problem/4737380>) + _private->zoomMultiplier = multiplier; + _private->zoomsTextOnly = isTextOnly; + + // FIXME: It might be nice to rework this code so that _private->zoomMultiplier doesn't exist + // and instead the zoom factors stored in Frame are used. + Frame* coreFrame = [self _mainCoreFrame]; + if (coreFrame) { + if (_private->zoomsTextOnly) + coreFrame->setPageAndTextZoomFactors(1, multiplier); + else + coreFrame->setPageAndTextZoomFactors(multiplier, 1); + } +} + +- (float)_zoomMultiplier:(BOOL)isTextOnly +{ + if (isTextOnly != [self _realZoomMultiplierIsTextOnly]) + return 1.0f; + return _private->zoomMultiplier; +} + +- (float)_realZoomMultiplier +{ + return _private->zoomMultiplier; +} + +- (BOOL)_realZoomMultiplierIsTextOnly +{ + if (!_private->page) + return NO; + + return _private->zoomsTextOnly; +} + +#define MinimumZoomMultiplier 0.5f +#define MaximumZoomMultiplier 3.0f +#define ZoomMultiplierRatio 1.2f + +- (BOOL)_canZoomOut:(BOOL)isTextOnly +{ + id docView = [[[self mainFrame] frameView] documentView]; + if ([docView conformsToProtocol:@protocol(_WebDocumentZooming)]) { + id <_WebDocumentZooming> zoomingDocView = (id <_WebDocumentZooming>)docView; + return [zoomingDocView _canZoomOut]; + } + return [self _zoomMultiplier:isTextOnly] / ZoomMultiplierRatio > MinimumZoomMultiplier; +} + + +- (BOOL)_canZoomIn:(BOOL)isTextOnly +{ + id docView = [[[self mainFrame] frameView] documentView]; + if ([docView conformsToProtocol:@protocol(_WebDocumentZooming)]) { + id <_WebDocumentZooming> zoomingDocView = (id <_WebDocumentZooming>)docView; + return [zoomingDocView _canZoomIn]; + } + return [self _zoomMultiplier:isTextOnly] * ZoomMultiplierRatio < MaximumZoomMultiplier; +} + +- (IBAction)_zoomOut:(id)sender isTextOnly:(BOOL)isTextOnly +{ + id docView = [[[self mainFrame] frameView] documentView]; + if ([docView conformsToProtocol:@protocol(_WebDocumentZooming)]) { + id <_WebDocumentZooming> zoomingDocView = (id <_WebDocumentZooming>)docView; + return [zoomingDocView _zoomOut:sender]; + } + float newScale = [self _zoomMultiplier:isTextOnly] / ZoomMultiplierRatio; + if (newScale > MinimumZoomMultiplier) + [self _setZoomMultiplier:newScale isTextOnly:isTextOnly]; +} + +- (IBAction)_zoomIn:(id)sender isTextOnly:(BOOL)isTextOnly +{ + id docView = [[[self mainFrame] frameView] documentView]; + if ([docView conformsToProtocol:@protocol(_WebDocumentZooming)]) { + id <_WebDocumentZooming> zoomingDocView = (id <_WebDocumentZooming>)docView; + return [zoomingDocView _zoomIn:sender]; + } + float newScale = [self _zoomMultiplier:isTextOnly] * ZoomMultiplierRatio; + if (newScale < MaximumZoomMultiplier) + [self _setZoomMultiplier:newScale isTextOnly:isTextOnly]; +} + +- (BOOL)_canResetZoom:(BOOL)isTextOnly +{ + id docView = [[[self mainFrame] frameView] documentView]; + if ([docView conformsToProtocol:@protocol(_WebDocumentZooming)]) { + id <_WebDocumentZooming> zoomingDocView = (id <_WebDocumentZooming>)docView; + return [zoomingDocView _canResetZoom]; + } + return [self _zoomMultiplier:isTextOnly] != 1.0f; +} + +- (IBAction)_resetZoom:(id)sender isTextOnly:(BOOL)isTextOnly +{ + id docView = [[[self mainFrame] frameView] documentView]; + if ([docView conformsToProtocol:@protocol(_WebDocumentZooming)]) { + id <_WebDocumentZooming> zoomingDocView = (id <_WebDocumentZooming>)docView; + return [zoomingDocView _resetZoom:sender]; + } + if ([self _zoomMultiplier:isTextOnly] != 1.0f) + [self _setZoomMultiplier:1.0f isTextOnly:isTextOnly]; +} + +- (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; + if (Frame* mainFrame = [self _mainCoreFrame]) + mainFrame->loader()->reloadWithOverrideEncoding(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 +{ + // Return statements are only valid in a function but some applications pass in scripts + // prefixed with return (<rdar://problems/5103720&4616860>) since older WebKit versions + // silently ignored the return. If the application is linked against an earlier version + // of WebKit we will strip the return so the script wont fail. + if (!WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_JAVASCRIPT_RETURN_QUIRK)) { + NSRange returnStringRange = [script rangeOfString:@"return "]; + if (returnStringRange.length && !returnStringRange.location) + script = [script substringFromIndex:returnStringRange.location + returnStringRange.length]; + } + + NSString *result = [[self mainFrame] _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 = [self _mainCoreFrame]; + if (!coreFrame) + return nil; + return coreFrame->script()->windowScriptObject(); +} + +// Get the appropriate user-agent string for a particular URL. +- (NSString *)userAgentForURL:(NSURL *)url +{ + if (_private->useSiteSpecificSpoofing) { + // No current site-specific spoofs. + } + + if (_private->userAgent.isNull()) + _private->userAgent = [[self class] _standardUserAgentWithApplicationName:_private->applicationNameForUserAgent]; + + return _private->userAgent; +} + +- (void)setHostWindow:(NSWindow *)hostWindow +{ + if (_private->closed && hostWindow) + return; + if (hostWindow == _private->hostWindow) + return; + + Frame* coreFrame = [self _mainCoreFrame]; + if (_private->usesDocumentViews) { + for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) + [[[kit(frame) frameView] documentView] viewWillMoveToHostWindow:hostWindow]; + } + if (_private->hostWindow && [self window] != _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]; + if (_private->usesDocumentViews) { + for (Frame* frame = coreFrame; frame; frame = frame->tree()->traverseNext(coreFrame)) + [[[kit(frame) frameView] documentView] viewDidMoveToHostWindow]; + } +} + +- (NSWindow *)hostWindow +{ + // -[WebView hostWindow] can sometimes be called from the WebView's [super dealloc] method + // so we check here to make sure it's not null. + if (!_private) + return nil; + + 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 to 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]; +} + +- (DragApplicationFlags)applicationFlags:(id <NSDraggingInfo>)draggingInfo +{ + uint32_t flags = 0; + if ([NSApp modalWindow]) + flags = DragApplicationIsModal; + if ([[self window] attachedSheet]) + flags |= DragApplicationHasAttachedSheet; + if ([draggingInfo draggingSource] == self) + flags |= DragApplicationIsSource; + if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) + flags |= DragApplicationIsCopyKeyDown; + return static_cast<DragApplicationFlags>(flags); +} + +- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)draggingInfo +{ + IntPoint client([draggingInfo draggingLocation]); + IntPoint global(globalPoint([draggingInfo draggingLocation], [self window])); + DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]); + return core(self)->dragController()->dragEntered(&dragData); +} + +- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)draggingInfo +{ + Page* page = core(self); + if (!page) + return NSDragOperationNone; + + IntPoint client([draggingInfo draggingLocation]); + IntPoint global(globalPoint([draggingInfo draggingLocation], [self window])); + DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]); + return page->dragController()->dragUpdated(&dragData); +} + +- (void)draggingExited:(id <NSDraggingInfo>)draggingInfo +{ + Page* page = core(self); + if (!page) + return; + + IntPoint client([draggingInfo draggingLocation]); + IntPoint global(globalPoint([draggingInfo draggingLocation], [self window])); + DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]); + page->dragController()->dragExited(&dragData); +} + +- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)draggingInfo +{ + return YES; +} + +- (BOOL)performDragOperation:(id <NSDraggingInfo>)draggingInfo +{ + IntPoint client([draggingInfo draggingLocation]); + IntPoint global(globalPoint([draggingInfo draggingLocation], [self window])); + DragData dragData(draggingInfo, client, global, static_cast<DragOperation>([draggingInfo draggingSourceOperationMask]), [self applicationFlags:draggingInfo]); + return core(self)->dragController()->performDrag(&dragData); +} + +- (NSView *)_hitTest:(NSPoint *)point dragTypes:(NSSet *)types +{ + NSView *hitView = [super _hitTest:point dragTypes:types]; + if (!hitView && [[self superview] mouse:*point inRect:[self frame]]) + return self; + return hitView; +} + +- (BOOL)acceptsFirstResponder +{ + if (_private->usesDocumentViews) + return [[[self mainFrame] frameView] acceptsFirstResponder]; + + // FIXME (Viewless): Need more code from WebHTMLView here. + return YES; +} + +- (BOOL)becomeFirstResponder +{ + if (_private->usesDocumentViews) { + 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; + } + return NO; + } + + if ([mainFrameView acceptsFirstResponder]) { + _private->becomingFirstResponder = YES; + _private->becomingFirstResponderFromOutside = fromOutside; + [window makeFirstResponder:mainFrameView]; + _private->becomingFirstResponderFromOutside = NO; + _private->becomingFirstResponder = NO; + return YES; + } + + return NO; + } + + // FIXME (Viewless): Need more code from WebHTMLView here. + return YES; +} + +- (NSView *)_webcore_effectiveFirstResponder +{ + if (_private && _private->usesDocumentViews) { + if (WebFrameView *frameView = [[self mainFrame] frameView]) + return [frameView _webcore_effectiveFirstResponder]; + } + return [super _webcore_effectiveFirstResponder]; +} + +- (void)setNextKeyView:(NSView *)view +{ + if (_private && _private->usesDocumentViews) { + // This works together with becomeFirstResponder to splice the WebView into + // the key loop similar to the way NSScrollView does this. Note that + // WebFrameView has similar code. + if (WebFrameView *mainFrameView = [[self mainFrame] frameView]) { + [mainFrameView setNextKeyView:view]; + return; + } + } + + [super setNextKeyView:view]; +} + +static WebFrame *incrementFrame(WebFrame *frame, WebFindOptions options = 0) +{ + Frame* coreFrame = core(frame); + return kit((options & WebFindOptionsBackwards) + ? coreFrame->tree()->traversePreviousWithWrap(options & WebFindOptionsWrapAround) + : coreFrame->tree()->traverseNextWithWrap(options & WebFindOptionsWrapAround)); +} + +- (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] _updateBackgroundAndUpdatesWhileOffscreen]; +} + +- (BOOL)drawsBackground +{ + // This method can be called beneath -[NSView dealloc] after we have cleared _private, + // indirectly via -[WebFrameView viewDidMoveToWindow]. + return !_private || _private->drawsBackground; +} + +- (void)setShouldUpdateWhileOffscreen:(BOOL)updateWhileOffscreen +{ + if (_private->shouldUpdateWhileOffscreen == updateWhileOffscreen) + return; + _private->shouldUpdateWhileOffscreen = updateWhileOffscreen; + [[self mainFrame] _updateBackgroundAndUpdatesWhileOffscreen]; +} + +- (BOOL)shouldUpdateWhileOffscreen +{ + return _private->shouldUpdateWhileOffscreen; +} + +- (void)setCurrentNodeHighlight:(WebNodeHighlight *)nodeHighlight +{ + id old = _private->currentNodeHighlight; + _private->currentNodeHighlight = [nodeHighlight retain]; + [old release]; +} + +- (WebNodeHighlight *)currentNodeHighlight +{ + return _private->currentNodeHighlight; +} + +- (NSView *)previousValidKeyView +{ + NSView *result = [super previousValidKeyView]; + + // Work around AppKit bug 6905484. If the result is a view that's inside this one, it's + // possible it is the wrong answer, because the fact that it's a descendant causes the + // code that implements key view redirection to fail; this means we won't redirect to + // the toolbar, for example, when we hit the edge of a window. Since the bug is specific + // to cases where the receiver of previousValidKeyView is an ancestor of the last valid + // key view in the loop, we can sidestep it by walking along previous key views until + // we find one that is not a superview, then using that to call previousValidKeyView. + + if (![result isDescendantOf:self]) + return result; + + // Use a visited set so we don't loop indefinitely when walking crazy key loops. + // AppKit uses such sets internally and we want our loop to be as robust as its loops. + RetainPtr<CFMutableSetRef> visitedViews = CFSetCreateMutable(0, 0, 0); + CFSetAddValue(visitedViews.get(), result); + + NSView *previousView = self; + do { + CFSetAddValue(visitedViews.get(), previousView); + previousView = [previousView previousKeyView]; + if (!previousView || CFSetGetValue(visitedViews.get(), previousView)) + return result; + } while ([result isDescendantOf:previousView]); + return [previousView previousValidKeyView]; +} + +@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 || _private->page->defersLoading()) + return NO; + + return !!_private->page->backForwardList()->backItem(); +} + +- (BOOL)canGoForward +{ + if (!_private->page || _private->page->defersLoading()) + 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]; +} + +- (IBAction)reloadFromOrigin:(id)sender +{ + [[self mainFrame] reloadFromOrigin]; +} + +// FIXME: This code should move into WebCore so that it is not duplicated in each WebKit. +// (This includes canMakeTextSmaller/Larger, makeTextSmaller/Larger, and canMakeTextStandardSize/makeTextStandardSize) +- (BOOL)canMakeTextSmaller +{ + return [self _canZoomOut:![[NSUserDefaults standardUserDefaults] boolForKey:WebKitDebugFullPageZoomPreferenceKey]]; +} + +- (IBAction)makeTextSmaller:(id)sender +{ + return [self _zoomOut:sender isTextOnly:![[NSUserDefaults standardUserDefaults] boolForKey:WebKitDebugFullPageZoomPreferenceKey]]; +} + +- (BOOL)canMakeTextLarger +{ + return [self _canZoomIn:![[NSUserDefaults standardUserDefaults] boolForKey:WebKitDebugFullPageZoomPreferenceKey]]; +} + +- (IBAction)makeTextLarger:(id)sender +{ + return [self _zoomIn:sender isTextOnly:![[NSUserDefaults standardUserDefaults] boolForKey:WebKitDebugFullPageZoomPreferenceKey]]; +} + +- (BOOL)canMakeTextStandardSize +{ + return [self _canResetZoom:![[NSUserDefaults standardUserDefaults] boolForKey:WebKitDebugFullPageZoomPreferenceKey]]; +} + +- (IBAction)makeTextStandardSize:(id)sender +{ + return [self _resetZoom:sender isTextOnly:![[NSUserDefaults standardUserDefaults] boolForKey:WebKitDebugFullPageZoomPreferenceKey]]; +} + +- (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; +} + +#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; + } else if (action == @selector(toggleSmartInsertDelete:)) { + BOOL checkMark = [self smartInsertDeleteEnabled]; + if ([(NSObject *)item isKindOfClass:[NSMenuItem class]]) { + NSMenuItem *menuItem = (NSMenuItem *)item; + [menuItem setState:checkMark ? NSOnState : NSOffState]; + } + return YES; +#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 +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + } else if (action == @selector(toggleAutomaticQuoteSubstitution:)) { + BOOL checkMark = [self isAutomaticQuoteSubstitutionEnabled]; + if ([(NSObject *)item isKindOfClass:[NSMenuItem class]]) { + NSMenuItem *menuItem = (NSMenuItem *)item; + [menuItem setState:checkMark ? NSOnState : NSOffState]; + } + return YES; + } else if (action == @selector(toggleAutomaticLinkDetection:)) { + BOOL checkMark = [self isAutomaticLinkDetectionEnabled]; + if ([(NSObject *)item isKindOfClass:[NSMenuItem class]]) { + NSMenuItem *menuItem = (NSMenuItem *)item; + [menuItem setState:checkMark ? NSOnState : NSOffState]; + } + return YES; + } else if (action == @selector(toggleAutomaticDashSubstitution:)) { + BOOL checkMark = [self isAutomaticDashSubstitutionEnabled]; + if ([(NSObject *)item isKindOfClass:[NSMenuItem class]]) { + NSMenuItem *menuItem = (NSMenuItem *)item; + [menuItem setState:checkMark ? NSOnState : NSOffState]; + } + return YES; + } else if (action == @selector(toggleAutomaticTextReplacement:)) { + BOOL checkMark = [self isAutomaticTextReplacementEnabled]; + if ([(NSObject *)item isKindOfClass:[NSMenuItem class]]) { + NSMenuItem *menuItem = (NSMenuItem *)item; + [menuItem setState:checkMark ? NSOnState : NSOffState]; + } + return YES; + } else if (action == @selector(toggleAutomaticSpellingCorrection:)) { + BOOL checkMark = [self isAutomaticSpellingCorrectionEnabled]; + 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) + +- (void)scheduleInRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode +{ + if (runLoop && mode) + core(self)->addSchedulePair(SchedulePair::create(runLoop, (CFStringRef)mode)); +} + +- (void)unscheduleFromRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode +{ + if (runLoop && mode) + core(self)->removeSchedulePair(SchedulePair::create(runLoop, (CFStringRef)mode)); +} + +static BOOL findString(NSView <WebDocumentSearching> *searchView, NSString *string, WebFindOptions options) +{ + if ([searchView conformsToProtocol:@protocol(WebDocumentOptionsSearching)]) + return [(NSView <WebDocumentOptionsSearching> *)searchView _findString:string options:options]; + if ([searchView conformsToProtocol:@protocol(WebDocumentIncrementalSearching)]) + return [(NSView <WebDocumentIncrementalSearching> *)searchView searchFor:string direction:!(options & WebFindOptionsBackwards) caseSensitive:!(options & WebFindOptionsCaseInsensitive) wrap:!!(options & WebFindOptionsWrapAround) startInSelection:!!(options & WebFindOptionsStartInSelection)]; + return [searchView searchFor:string direction:!(options & WebFindOptionsBackwards) caseSensitive:!(options & WebFindOptionsCaseInsensitive) wrap:!!(options & WebFindOptionsWrapAround)]; +} + +- (BOOL)findString:(NSString *)string options:(WebFindOptions)options +{ + 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, options); + + 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; + + // 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 the wrap option through + // here, and then bailing out before we get to the code that would search again in the + // same content. + WebFindOptions optionsForThisPass = onlyOneFrame ? options : (options & ~WebFindOptionsWrapAround); + + if (findString(searchView, string, optionsForThisPass)) { + 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 WebFindOptionsWrapAround is set 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 WebFindOptionsWrapAround. When there are no matches, this will search + // 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 ((options & WebFindOptionsWrapAround) && startSearchView) { + if (findString(startSearchView, string, options)) { + [[self window] makeFirstResponder:startSearchView]; + return YES; + } + } + return NO; +} + +- (void)setHoverFeedbackSuspended:(BOOL)newValue +{ + if (_private->hoverFeedbackSuspended == newValue) + return; + + _private->hoverFeedbackSuspended = newValue; + + if (_private->usesDocumentViews) { + 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]; + return; + } + + [self _updateMouseoverWithFakeEvent]; +} + +- (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; + [self _cacheScriptDebugDelegateImplementations]; + + if (delegate) + [self _attachScriptDebuggerToAllFrames]; + else + [self _detachScriptDebuggerFromAllFrames]; +} + +- (id)scriptDebugDelegate +{ + return _private->scriptDebugDelegate; +} + +- (void)setHistoryDelegate:(id)delegate +{ + _private->historyDelegate = delegate; + [self _cacheHistoryDelegateImplementations]; +} + +- (id)historyDelegate +{ + return _private->historyDelegate; +} + +- (BOOL)shouldClose +{ + Frame* coreFrame = [self _mainCoreFrame]; + if (!coreFrame) + return YES; + return coreFrame->loader()->shouldClose(); +} + +static NSAppleEventDescriptor* aeDescFromJSValue(ExecState* exec, JSValue jsValue) +{ + NSAppleEventDescriptor* aeDesc = 0; + if (jsValue.isBoolean()) + return [NSAppleEventDescriptor descriptorWithBoolean:jsValue.getBoolean()]; + if (jsValue.isString()) + return [NSAppleEventDescriptor descriptorWithString:ustringToString(jsValue.getString(exec))]; + if (jsValue.isNumber()) { + double value = jsValue.uncheckedGetNumber(); + int intValue = value; + if (value == intValue) + return [NSAppleEventDescriptor descriptorWithDescriptorType:typeSInt32 bytes:&intValue length:sizeof(intValue)]; + return [NSAppleEventDescriptor descriptorWithDescriptorType:typeIEEE64BitFloatingPoint bytes:&value length:sizeof(value)]; + } + if (jsValue.isObject()) { + JSObject* object = jsValue.getObject(); + if (object->inherits(&DateInstance::info)) { + DateInstance* date = static_cast<DateInstance*>(object); + double ms = date->internalNumber(); + if (!isnan(ms)) { + CFAbsoluteTime utcSeconds = ms / 1000 - kCFAbsoluteTimeIntervalSince1970; + LongDateTime ldt; + if (noErr == UCConvertCFAbsoluteTimeToLongDateTime(utcSeconds, &ldt)) + return [NSAppleEventDescriptor descriptorWithDescriptorType:typeLongDateTime bytes:&ldt length:sizeof(ldt)]; + } + } + else if (object->inherits(&JSArray::info)) { + DEFINE_STATIC_LOCAL(HashSet<JSObject*>, visitedElems, ()); + if (!visitedElems.contains(object)) { + visitedElems.add(object); + + JSArray* array = static_cast<JSArray*>(object); + aeDesc = [NSAppleEventDescriptor listDescriptor]; + unsigned numItems = array->length(); + for (unsigned i = 0; i < numItems; ++i) + [aeDesc insertDescriptor:aeDescFromJSValue(exec, array->get(exec, i)) atIndex:0]; + + visitedElems.remove(object); + return aeDesc; + } + } + JSValue primitive = object->toPrimitive(exec); + if (exec->hadException()) { + exec->clearException(); + return [NSAppleEventDescriptor nullDescriptor]; + } + return aeDescFromJSValue(exec, primitive); + } + if (jsValue.isUndefined()) + return [NSAppleEventDescriptor descriptorWithTypeCode:cMissingValue]; + ASSERT(jsValue.isNull()); + return [NSAppleEventDescriptor nullDescriptor]; +} + +- (NSAppleEventDescriptor *)aeDescByEvaluatingJavaScriptFromString:(NSString *)script +{ + Frame* coreFrame = [self _mainCoreFrame]; + if (!coreFrame) + return nil; + if (!coreFrame->document()) + return nil; + JSValue result = coreFrame->script()->executeScript(script, true).jsValue(); + if (!result) // FIXME: pass errors + return 0; + JSLock lock(SilenceAssertionsOnly); + return aeDescFromJSValue(coreFrame->script()->globalObject(mainThreadNormalWorld())->globalExec(), result); +} + +- (BOOL)canMarkAllTextMatches +{ + if (_private->closed) + return NO; + + WebFrame *frame = [self mainFrame]; + do { + id <WebDocumentView> view = [[frame frameView] documentView]; + if (view && ![view conformsToProtocol:@protocol(WebMultipleTextMatches)]) + return NO; + + frame = incrementFrame(frame); + } while (frame); + + return YES; +} + +- (NSUInteger)countMatchesForText:(NSString *)string options:(WebFindOptions)options highlight:(BOOL)highlight limit:(NSUInteger)limit markMatches:(BOOL)markMatches +{ + return [self countMatchesForText:string inDOMRange:nil options:options highlight:highlight limit:limit markMatches:markMatches]; +} + +- (NSUInteger)countMatchesForText:(NSString *)string inDOMRange:(DOMRange *)range options:(WebFindOptions)options highlight:(BOOL)highlight limit:(NSUInteger)limit markMatches:(BOOL)markMatches +{ + if (_private->closed) + return 0; + + WebFrame *frame = [self mainFrame]; + unsigned matchCount = 0; + do { + id <WebDocumentView> view = [[frame frameView] documentView]; + if ([view conformsToProtocol:@protocol(WebMultipleTextMatches)]) { + if (markMatches) + [(NSView <WebMultipleTextMatches>*)view setMarkedTextMatchesAreHighlighted:highlight]; + + ASSERT(limit == 0 || matchCount < limit); + matchCount += [(NSView <WebMultipleTextMatches>*)view countMatchesForText:string inDOMRange:range options:options limit:(limit == 0 ? 0 : limit - matchCount) markMatches:markMatches]; + + // Stop looking if we've reached the limit. A limit of 0 means no limit. + if (limit > 0 && matchCount >= limit) + break; + } + + frame = incrementFrame(frame); + } while (frame); + + return matchCount; +} + +- (void)unmarkAllTextMatches +{ + if (_private->closed) + return; + + WebFrame *frame = [self mainFrame]; + do { + id <WebDocumentView> view = [[frame frameView] documentView]; + if ([view conformsToProtocol:@protocol(WebMultipleTextMatches)]) + [(NSView <WebMultipleTextMatches>*)view unmarkAllTextMatches]; + + frame = incrementFrame(frame); + } while (frame); +} + +- (NSArray *)rectsForTextMatches +{ + if (_private->closed) + return [NSArray array]; + + 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); + } while (frame); + + return result; +} + +- (void)scrollDOMRangeToVisible:(DOMRange *)range +{ + [[[[range startContainer] ownerDocument] webFrame] _scrollDOMRangeToVisible:range]; +} + +- (BOOL)allowsUndo +{ + return _private->allowsUndo; +} + +- (void)setAllowsUndo:(BOOL)flag +{ + _private->allowsUndo = flag; +} + +- (void)setPageSizeMultiplier:(float)m +{ + [self _setZoomMultiplier:m isTextOnly:NO]; +} + +- (float)pageSizeMultiplier +{ + return ![self _realZoomMultiplierIsTextOnly] ? _private->zoomMultiplier : 1.0f; +} + +- (BOOL)canZoomPageIn +{ + return [self _canZoomIn:NO]; +} + +- (IBAction)zoomPageIn:(id)sender +{ + return [self _zoomIn:sender isTextOnly:NO]; +} + +- (BOOL)canZoomPageOut +{ + return [self _canZoomOut:NO]; +} + +- (IBAction)zoomPageOut:(id)sender +{ + return [self _zoomOut:sender isTextOnly:NO]; +} + +- (BOOL)canResetPageZoom +{ + return [self _canResetZoom:NO]; +} + +- (IBAction)resetPageZoom:(id)sender +{ + return [self _resetZoom:sender isTextOnly:NO]; +} + +- (void)setMediaVolume:(float)volume +{ + if (_private->page) + _private->page->setMediaVolume(volume); +} + +- (float)mediaVolume +{ + if (!_private->page) + return 0; + + return _private->page->mediaVolume(); +} + +- (void)addVisitedLinks:(NSArray *)visitedLinks +{ + PageGroup& group = core(self)->group(); + + NSEnumerator *enumerator = [visitedLinks objectEnumerator]; + while (NSString *url = [enumerator nextObject]) { + size_t length = [url length]; + const UChar* characters = CFStringGetCharactersPtr(reinterpret_cast<CFStringRef>(url)); + if (characters) + group.addVisitedLink(characters, length); + else { + Vector<UChar, 512> buffer(length); + [url getCharacters:buffer.data()]; + group.addVisitedLink(buffer.data(), length); + } + } +} + +@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->selection()->clear(); + else { + // Derive the frame to use from the range passed in. + // Using _selectedOrMainFrame could give us a different document than + // the one the range uses. + coreFrame = core([range startContainer])->document()->frame(); + if (!coreFrame) + return; + + coreFrame->selection()->setSelectedRange(core(range), core(selectionAffinity), true); + } +} + +- (DOMRange *)selectedDOMRange +{ + Frame* coreFrame = core([self _selectedOrMainFrame]); + if (!coreFrame) + return nil; + return kit(coreFrame->selection()->toNormalizedRange().get()); +} + +- (NSSelectionAffinity)selectionAffinity +{ + Frame* coreFrame = core([self _selectedOrMainFrame]); + if (!coreFrame) + return NSSelectionAffinityDownstream; + return kit(coreFrame->selection()->affinity()); +} + +- (void)setEditable:(BOOL)flag +{ + if (_private->editable != flag) { + _private->editable = flag; + if (!_private->tabKeyCyclesThroughElementsChanged && _private->page) + _private->page->setTabKeyCyclesThroughElements(!flag); + Frame* mainFrame = [self _mainCoreFrame]; + if (mainFrame) { + if (flag) { + mainFrame->editor()->applyEditingStyleToBodyElement(); + // If the WebView is made editable and the selection is empty, set it to something. + if (![self selectedDOMRange]) + mainFrame->selection()->setSelectionFromNone(); + } + } + } +} + +- (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 _selectedOrMainFrame] _setTypingStyle:style withUndoAction:EditActionUnspecified]; +} + +- (DOMCSSStyleDeclaration *)typingStyle +{ + return [[self _selectedOrMainFrame] _typingStyle]; +} + +- (void)setSmartInsertDeleteEnabled:(BOOL)flag +{ + if (_private->smartInsertDeleteEnabled != flag) { + _private->smartInsertDeleteEnabled = flag; + [[NSUserDefaults standardUserDefaults] setBool:_private->smartInsertDeleteEnabled forKey:WebSmartInsertDeleteEnabled]; + } + if (flag) + [self setSelectTrailingWhitespaceEnabled:false]; +} + +- (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]; + +#ifndef BUILDING_ON_LEOPARD + [[NSSpellChecker sharedSpellChecker] updatePanels]; +#else + NSSpellChecker *spellChecker = [NSSpellChecker sharedSpellChecker]; + if ([spellChecker respondsToSelector:@selector(_updateGrammar)]) + [spellChecker performSelector:@selector(_updateGrammar)]; +#endif + + // 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 (WebViewTextChecking) + +- (BOOL)isAutomaticQuoteSubstitutionEnabled +{ +#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) + return NO; +#else + return automaticQuoteSubstitutionEnabled; +#endif +} + +- (BOOL)isAutomaticLinkDetectionEnabled +{ +#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) + return NO; +#else + return automaticLinkDetectionEnabled; +#endif +} + +- (BOOL)isAutomaticDashSubstitutionEnabled +{ +#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) + return NO; +#else + return automaticDashSubstitutionEnabled; +#endif +} + +- (BOOL)isAutomaticTextReplacementEnabled +{ +#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) + return NO; +#else + return automaticTextReplacementEnabled; +#endif +} + +- (BOOL)isAutomaticSpellingCorrectionEnabled +{ +#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) + return NO; +#else + return automaticSpellingCorrectionEnabled; +#endif +} + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + +- (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag +{ + if (automaticQuoteSubstitutionEnabled == flag) + return; + automaticQuoteSubstitutionEnabled = flag; + [[NSUserDefaults standardUserDefaults] setBool:automaticQuoteSubstitutionEnabled forKey:WebAutomaticQuoteSubstitutionEnabled]; + [[NSSpellChecker sharedSpellChecker] updatePanels]; +} + +- (void)toggleAutomaticQuoteSubstitution:(id)sender +{ + [self setAutomaticQuoteSubstitutionEnabled:![self isAutomaticQuoteSubstitutionEnabled]]; +} + +- (void)setAutomaticLinkDetectionEnabled:(BOOL)flag +{ + if (automaticLinkDetectionEnabled == flag) + return; + automaticLinkDetectionEnabled = flag; + [[NSUserDefaults standardUserDefaults] setBool:automaticLinkDetectionEnabled forKey:WebAutomaticLinkDetectionEnabled]; + [[NSSpellChecker sharedSpellChecker] updatePanels]; +} + +- (void)toggleAutomaticLinkDetection:(id)sender +{ + [self setAutomaticLinkDetectionEnabled:![self isAutomaticLinkDetectionEnabled]]; +} + +- (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag +{ + if (automaticDashSubstitutionEnabled == flag) + return; + automaticDashSubstitutionEnabled = flag; + [[NSUserDefaults standardUserDefaults] setBool:automaticDashSubstitutionEnabled forKey:WebAutomaticDashSubstitutionEnabled]; + [[NSSpellChecker sharedSpellChecker] updatePanels]; +} + +- (void)toggleAutomaticDashSubstitution:(id)sender +{ + [self setAutomaticDashSubstitutionEnabled:![self isAutomaticDashSubstitutionEnabled]]; +} + +- (void)setAutomaticTextReplacementEnabled:(BOOL)flag +{ + if (automaticTextReplacementEnabled == flag) + return; + automaticTextReplacementEnabled = flag; + [[NSUserDefaults standardUserDefaults] setBool:automaticTextReplacementEnabled forKey:WebAutomaticTextReplacementEnabled]; + [[NSSpellChecker sharedSpellChecker] updatePanels]; +} + +- (void)toggleAutomaticTextReplacement:(id)sender +{ + [self setAutomaticTextReplacementEnabled:![self isAutomaticTextReplacementEnabled]]; +} + +- (void)setAutomaticSpellingCorrectionEnabled:(BOOL)flag +{ + if (automaticSpellingCorrectionEnabled == flag) + return; + automaticSpellingCorrectionEnabled = flag; + [[NSUserDefaults standardUserDefaults] setBool:automaticSpellingCorrectionEnabled forKey:WebAutomaticSpellingCorrectionEnabled]; + [[NSSpellChecker sharedSpellChecker] updatePanels]; +} + +- (void)toggleAutomaticSpellingCorrection:(id)sender +{ + [self setAutomaticSpellingCorrectionEnabled:![self isAutomaticSpellingCorrectionEnabled]]; +} + +#endif + +@end + +@implementation WebView (WebViewUndoableEditing) + +- (void)replaceSelectionWithNode:(DOMNode *)node +{ + [[self _selectedOrMainFrame] _replaceSelectionWithNode:node selectReplacement:YES smartReplace:NO matchStyle:NO]; +} + +- (void)replaceSelectionWithText:(NSString *)text +{ + [[self _selectedOrMainFrame] _replaceSelectionWithText:text selectReplacement:YES smartReplace:NO]; +} + +- (void)replaceSelectionWithMarkupString:(NSString *)markupString +{ + [[self _selectedOrMainFrame] _replaceSelectionWithMarkupString:markupString baseURLString:nil selectReplacement:YES smartReplace:NO]; +} + +- (void)replaceSelectionWithArchive:(WebArchive *)archive +{ + [[[self _selectedOrMainFrame] _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 _selectedOrMainFrame] _insertParagraphSeparatorInQuotedContent]; +} + +- (void)_replaceSelectionWithNode:(DOMNode *)node matchStyle:(BOOL)matchStyle +{ + [[self _selectedOrMainFrame] _replaceSelectionWithNode:node selectReplacement:YES smartReplace:NO matchStyle:matchStyle]; +} + +- (BOOL)_selectionIsCaret +{ + Frame* coreFrame = core([self _selectedOrMainFrame]); + if (!coreFrame) + return NO; + return coreFrame->selection()->isCaret(); +} + +- (BOOL)_selectionIsAll +{ + Frame* coreFrame = core([self _selectedOrMainFrame]); + if (!coreFrame) + return NO; + return coreFrame->selection()->isAll(MayLeaveEditableContent); +} + +@end + +static WebFrameView *containingFrameView(NSView *view) +{ + while (view && ![view isKindOfClass:[WebFrameView class]]) + view = [view superview]; + return (WebFrameView *)view; +} + +@implementation WebView (WebFileInternal) + +static inline uint64_t roundUpToPowerOf2(uint64_t num) +{ + return powf(2.0, ceilf(log2f(num))); +} + ++ (void)_setCacheModel:(WebCacheModel)cacheModel +{ + if (s_didSetCacheModel && cacheModel == s_cacheModel) + return; + + NSString *nsurlCacheDirectory = (NSString *)WebCFAutorelease(WKCopyFoundationCacheDirectory()); + if (!nsurlCacheDirectory) + nsurlCacheDirectory = NSHomeDirectory(); + + static uint64_t memSize = roundUpToPowerOf2(WebMemorySize() / 1024 / 1024); + unsigned long long diskFreeSize = WebVolumeFreeSize(nsurlCacheDirectory) / 1024 / 1000; + NSURLCache *nsurlCache = [NSURLCache sharedURLCache]; + + unsigned cacheTotalCapacity = 0; + unsigned cacheMinDeadCapacity = 0; + unsigned cacheMaxDeadCapacity = 0; + double deadDecodedDataDeletionInterval = 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 = 128 * 1024 * 1024; + else if (memSize >= 2048) + cacheTotalCapacity = 96 * 1024 * 1024; + else if (memSize >= 1024) + cacheTotalCapacity = 32 * 1024 * 1024; + else if (memSize >= 512) + 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 = 128 * 1024 * 1024; + else if (memSize >= 2048) + cacheTotalCapacity = 96 * 1024 * 1024; + else if (memSize >= 1024) + cacheTotalCapacity = 32 * 1024 * 1024; + else if (memSize >= 512) + 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 >= 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 = 192 * 1024 * 1024; + else if (memSize >= 2048) + cacheTotalCapacity = 128 * 1024 * 1024; + else if (memSize >= 1024) + cacheTotalCapacity = 64 * 1024 * 1024; + else if (memSize >= 512) + 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); + + deadDecodedDataDeletionInterval = 60; + + // 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 diskCapacity]; +#endif + + // Don't shrink a big disk cache, since that would cause churn. + nsurlCacheDiskCapacity = max(nsurlCacheDiskCapacity, [nsurlCache diskCapacity]); + + memoryCache()->setCapacities(cacheMinDeadCapacity, cacheMaxDeadCapacity, cacheTotalCapacity); + memoryCache()->setDeadDecodedDataDeletionInterval(deadDecodedDataDeletionInterval); + 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; +} + +- (BOOL)_isLoading +{ + WebFrame *mainFrame = [self mainFrame]; + return [[mainFrame _dataSource] isLoading] + || [[mainFrame provisionalDataSource] isLoading]; +} + +- (WebFrameView *)_frameViewAtWindowPoint:(NSPoint)point +{ + if (_private->closed) + return nil; + ASSERT(_private->usesDocumentViews); + 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]]); + + WebDataSource *dataSource = [(WebFrame *)[element objectForKey:WebElementFrameKey] dataSource]; + NSURLRequest *request = [[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]) + return; + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + [[NSWorkspace sharedWorkspace] showSearchResultsForQueryString:selectedString]; +#else + (void)HISearchWindowShow((CFStringRef)selectedString, kNilOptions); +#endif +} + +#if USE(ACCELERATED_COMPOSITING) +- (void)_clearLayerSyncLoopObserver +{ + if (!_private->layerSyncRunLoopObserver) + return; + + CFRunLoopObserverInvalidate(_private->layerSyncRunLoopObserver); + CFRelease(_private->layerSyncRunLoopObserver); + _private->layerSyncRunLoopObserver = 0; +} +#endif + +#if ENABLE(GLIB_SUPPORT) +- (void)_clearGlibLoopObserver +{ + if (!_private->glibRunLoopObserver) + return; + + CFRunLoopObserverInvalidate(_private->glibRunLoopObserver); + CFRelease(_private->glibRunLoopObserver); + _private->glibRunLoopObserver = 0; +} +#endif +@end + +@implementation WebView (WebViewInternal) + ++ (BOOL)shouldIncludeInWebKitStatistics +{ + return NO; +} + +- (BOOL)_becomingFirstResponderFromOutside +{ + return _private->becomingFirstResponderFromOutside; +} + +#if ENABLE(ICONDATABASE) +- (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* cache = &_private->frameLoadDelegateImplementations; + if (cache->didReceiveIconForFrameFunc) { + Image* image = iconDatabase()->iconForPageURL(core(webFrame)->loader()->url().string(), IntSize(16, 16)); + if (NSImage *icon = webGetNSImage(image, NSMakeSize(16, 16))) + CallFrameLoadDelegate(cache->didReceiveIconForFrameFunc, self, @selector(webView:didReceiveIcon:forFrame:), icon, webFrame); + } + + [self _didChangeValueForKey:_WebMainFrameIconKey]; +} +#endif // ENABLE(ICONDATABASE) + +- (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 +{ + ASSERT(_private->identifierMap.contains(identifier)); + _private->identifierMap.remove(identifier); + + // 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); +} + +- (void)_retrieveKeyboardUIModeFromPreferences:(NSNotification *)notification +{ + CFPreferencesAppSynchronize(UniversalAccessDomain); + + Boolean keyExistsAndHasValidFormat; + int mode = CFPreferencesGetAppIntegerValue(AppleKeyboardUIMode, UniversalAccessDomain, &keyExistsAndHasValidFormat); + + // The keyboard access mode is reported by two bits: + // Bit 0 is set if feature is on + // Bit 1 is set if full keyboard access works for any control, not just text boxes and lists + // We require both bits to be on. + // I do not know that we would ever get one bit on and the other off since + // checking the checkbox in system preferences which is marked as "Turn on full keyboard access" + // turns on both bits. + _private->_keyboardUIMode = (mode & 0x2) ? KeyboardAccessFull : KeyboardAccessDefault; + + // check for tabbing to links + if ([_private->preferences tabsToLinks]) + _private->_keyboardUIMode = (KeyboardUIMode)(_private->_keyboardUIMode | KeyboardAccessTabsToLinks); +} + +- (KeyboardUIMode)_keyboardUIMode +{ + if (!_private->_keyboardUIModeAccessed) { + _private->_keyboardUIModeAccessed = YES; + + [self _retrieveKeyboardUIModeFromPreferences:nil]; + + [[NSDistributedNotificationCenter defaultCenter] + addObserver:self selector:@selector(_retrieveKeyboardUIModeFromPreferences:) + name:KeyboardUIModeDidChangeNotification object:nil]; + + [[NSNotificationCenter defaultCenter] + addObserver:self selector:@selector(_retrieveKeyboardUIModeFromPreferences:) + name:WebPreferencesChangedNotification object:nil]; + } + return _private->_keyboardUIMode; +} + +- (void)_setInsertionPasteboard:(NSPasteboard *)pasteboard +{ + _private->insertionPasteboard = pasteboard; +} + +- (void)_selectionChanged +{ + if (_private->usesDocumentViews) { + id documentView = [[[self _selectedOrMainFrame] frameView] documentView]; + if ([documentView isKindOfClass:[WebHTMLView class]]) + [documentView _selectionChanged]; + return; + } + + // FIXME (Viewless): We'll need code here. +} + +- (Frame*)_mainCoreFrame +{ + return (_private && _private->page) ? _private->page->mainFrame() : 0; +} + +- (WebFrame *)_selectedOrMainFrame +{ + WebFrame *result = [self selectedFrame]; + if (result == nil) + result = [self mainFrame]; + return result; +} + +#if USE(ACCELERATED_COMPOSITING) + +- (BOOL)_needsOneShotDrawingSynchronization +{ + return _private->needsOneShotDrawingSynchronization; +} + +- (void)_setNeedsOneShotDrawingSynchronization:(BOOL)needsSynchronization +{ + _private->needsOneShotDrawingSynchronization = needsSynchronization; +} + +- (BOOL)_syncCompositingChanges +{ + Frame* frame = [self _mainCoreFrame]; + if (frame && frame->view()) + return frame->view()->syncCompositingStateRecursive(); + + return YES; +} + +/* + The order of events with compositing updates is this: + + Start of runloop End of runloop + | | + --|-------------------------------------------------------|-- + ^ ^ ^ + | | | + NSWindow update, | CA commit + NSView drawing | + flush | + layerSyncRunLoopObserverCallBack + + To avoid flashing, we have to ensure that compositing changes (rendered via + the CoreAnimation rendering display link) appear on screen at the same time + as content painted into the window via the normal WebCore rendering path. + + CoreAnimation will commit any layer changes at the end of the runloop via + its "CA commit" observer. Those changes can then appear onscreen at any time + when the display link fires, which can result in unsynchronized rendering. + + To fix this, the GraphicsLayerCA code in WebCore does not change the CA + layer tree during style changes and layout; it stores up all changes and + commits them via syncCompositingState(). There are then two situations in + which we can call syncCompositingState(): + + 1. When painting. FrameView::paintContents() makes a call to syncCompositingState(). + + 2. When style changes/layout have made changes to the layer tree which do not + result in painting. In this case we need a run loop observer to do a + syncCompositingState() at an appropriate time. The observer will keep firing + until the time is right (essentially when there are no more pending layouts). + +*/ + +static void layerSyncRunLoopObserverCallBack(CFRunLoopObserverRef, CFRunLoopActivity, void* info) +{ + WebView *webView = reinterpret_cast<WebView*>(info); + NSWindow *window = [webView window]; + + // An NSWindow may not display in the next runloop cycle after dirtying due to delayed window display logic, + // in which case this observer can fire first. So if the window is due for a display, don't commit + // layer changes, otherwise they'll show on screen before the view drawing. + bool viewsNeedDisplay; +#ifndef __LP64__ + if (window && [window _wrapsCarbonWindow]) + viewsNeedDisplay = HIViewGetNeedsDisplay(HIViewGetRoot(static_cast<WindowRef>([window windowRef]))); + else +#endif + viewsNeedDisplay = [window viewsNeedDisplay]; + + if (viewsNeedDisplay) + return; + + if ([webView _syncCompositingChanges]) { + [webView _clearLayerSyncLoopObserver]; + // AppKit may have disabled screen updates, thinking an upcoming window flush will re-enable them. + // In case setNeedsDisplayInRect() has prevented the window from needing to be flushed, re-enable screen + // updates here. + if (![window isFlushWindowDisabled]) + [window _enableScreenUpdatesIfNeeded]; + } else { + // Since the WebView does not need display, -viewWillDraw will not be called. Perform pending layout now, + // so that the layers draw with up-to-date layout. + [webView _viewWillDrawInternal]; + } +} + +- (void)_scheduleCompositingLayerSync +{ + CFRunLoopRef currentRunLoop = CFRunLoopGetCurrent(); + + // Make sure we wake up the loop or the observer could be delayed until some other source fires. + CFRunLoopWakeUp(currentRunLoop); + + if (_private->layerSyncRunLoopObserver) + return; + + // Run after AppKit does its window update. If we do any painting, we'll commit + // layer changes from FrameView::paintContents(), otherwise we'll commit via + // _syncCompositingChanges when this observer fires. + const CFIndex runLoopOrder = NSDisplayWindowRunLoopOrdering + 1; + + // The WebView always outlives the observer, so no need to retain/release. + CFRunLoopObserverContext context = { 0, self, 0, 0, 0 }; + + _private->layerSyncRunLoopObserver = CFRunLoopObserverCreate(NULL, + kCFRunLoopBeforeWaiting | kCFRunLoopExit, true /* repeats */, + runLoopOrder, layerSyncRunLoopObserverCallBack, &context); + + CFRunLoopAddObserver(currentRunLoop, _private->layerSyncRunLoopObserver, kCFRunLoopCommonModes); +} + +#endif + +#if ENABLE(VIDEO) + +- (void)_enterFullscreenForNode:(WebCore::Node*)node +{ + ASSERT(node->hasTagName(WebCore::HTMLNames::videoTag)); + HTMLMediaElement* videoElement = static_cast<HTMLMediaElement*>(node); + + if (_private->fullscreenController) { + if ([_private->fullscreenController mediaElement] == videoElement) { + // The backend may just warn us that the underlaying plaftormMovie() + // has changed. Just force an update. + [_private->fullscreenController setMediaElement:videoElement]; + return; // No more to do. + } + + // First exit Fullscreen for the old mediaElement. + [_private->fullscreenController mediaElement]->exitFullscreen(); + // This previous call has to trigger _exitFullscreen, + // which has to clear _private->fullscreenController. + ASSERT(!_private->fullscreenController); + } + if (!_private->fullscreenController) { + _private->fullscreenController = [[WebVideoFullscreenController alloc] init]; + [_private->fullscreenController setMediaElement:videoElement]; + [_private->fullscreenController enterFullscreen:[[self window] screen]]; + } + else + [_private->fullscreenController setMediaElement:videoElement]; +} + +- (void)_exitFullscreen +{ + if (!_private->fullscreenController) + return; + [_private->fullscreenController exitFullscreen]; + [_private->fullscreenController release]; + _private->fullscreenController = nil; +} + +#endif + +#if ENABLE(FULLSCREEN_API) +- (BOOL)_supportsFullScreenForElement:(const WebCore::Element*)element +{ + if (![[WebPreferences standardPreferences] fullScreenEnabled]) + return NO; + + // FIXME: If the element is in an IFrame, we should ensure it has + // an AllowsFullScreen=YES attribute before allowing fullscreen access. + return YES; +} + +- (void)_enterFullScreenForElement:(WebCore::Element*)element +{ + if (!_private->newFullscreenController) + _private->newFullscreenController = [[WebFullScreenController alloc] init]; + + [_private->newFullscreenController setElement:element]; + [_private->newFullscreenController setWebView:self]; + [_private->newFullscreenController enterFullscreen:[[self window] screen]]; +} + +- (void)_exitFullScreenForElement:(WebCore::Element*)element +{ + if (!_private->newFullscreenController) + return; + [_private->newFullscreenController exitFullscreen]; +} + +- (void)_fullScreenRendererChanged:(WebCore::RenderBox*)renderer +{ + if (!_private->newFullscreenController) + _private->newFullscreenController = [[WebFullScreenController alloc] init]; + [_private->newFullscreenController setRenderer:renderer]; +} +#endif + +#if ENABLE(GLIB_SUPPORT) + +static void glibContextIterationCallback(CFRunLoopObserverRef, CFRunLoopActivity, void*) +{ + g_main_context_iteration(0, FALSE); +} + +- (void)_scheduleGlibContextIterations +{ + if (_private->glibRunLoopObserver) + return; + + NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop]; + + // Create a run loop observer and attach it to the run loop. + CFRunLoopObserverContext context = {0, self, 0, 0, 0}; + _private->glibRunLoopObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, YES, 0, &glibContextIterationCallback, &context); + + if (_private->glibRunLoopObserver) { + CFRunLoopRef cfLoop = [myRunLoop getCFRunLoop]; + CFRunLoopAddObserver(cfLoop, _private->glibRunLoopObserver, kCFRunLoopDefaultMode); + } + +} +#endif + + +@end + +@implementation WebView (WebViewDeviceOrientation) + +- (void)_setDeviceOrientationProvider:(id<WebDeviceOrientationProvider>)deviceOrientationProvider +{ + if (_private) + _private->m_deviceOrientationProvider = deviceOrientationProvider; +} + +- (id<WebDeviceOrientationProvider>)_deviceOrientationProvider +{ + if (_private) + return _private->m_deviceOrientationProvider; + return nil; +} + +@end + +@implementation WebView (WebViewGeolocation) + +- (void)_setGeolocationProvider:(id<WebGeolocationProvider>)geolocationProvider +{ + if (_private) + _private->_geolocationProvider = geolocationProvider; +} + +- (id<WebGeolocationProvider>)_geolocationProvider +{ + if (_private) + return _private->_geolocationProvider; + return nil; +} + +- (void)_geolocationDidChangePosition:(WebGeolocationPosition *)position +{ +#if ENABLE(CLIENT_BASED_GEOLOCATION) + if (_private && _private->page) + _private->page->geolocationController()->positionChanged(core(position)); +#endif +} + +- (void)_geolocationDidFailWithError:(NSError *)error +{ +#if ENABLE(CLIENT_BASED_GEOLOCATION) + if (_private && _private->page) { + RefPtr<GeolocationError> geolocatioError = GeolocationError::create(GeolocationError::PositionUnavailable, [error localizedDescription]); + _private->page->geolocationController()->errorOccurred(geolocatioError.get()); + } +#endif +} + +@end + +@implementation WebView (WebViewPrivateStyleInfo) + +- (JSValueRef)_computedStyleIncludingVisitedInfo:(JSContextRef)context forElement:(JSValueRef)value +{ + JSLock lock(SilenceAssertionsOnly); + ExecState* exec = toJS(context); + if (!value) + return JSValueMakeUndefined(context); + JSValue jsValue = toJS(exec, value); + if (!jsValue.inherits(&JSElement::s_info)) + return JSValueMakeUndefined(context); + JSElement* jsElement = static_cast<JSElement*>(asObject(jsValue)); + Element* element = jsElement->impl(); + RefPtr<CSSComputedStyleDeclaration> style = computedStyle(element, true); + return toRef(exec, toJS(exec, jsElement->globalObject(), style.get())); +} + +@end + +@implementation WebView (WebViewPrivateNodesFromRect) + +- (JSValueRef)_nodesFromRect:(JSContextRef)context forDocument:(JSValueRef)value x:(int)x y:(int)y top:(unsigned)top right:(unsigned)right bottom:(unsigned)bottom left:(unsigned)left ignoreClipping:(BOOL)ignoreClipping +{ + JSLock lock(SilenceAssertionsOnly); + ExecState* exec = toJS(context); + if (!value) + return JSValueMakeUndefined(context); + JSValue jsValue = toJS(exec, value); + if (!jsValue.inherits(&JSDocument::s_info)) + return JSValueMakeUndefined(context); + JSDocument* jsDocument = static_cast<JSDocument*>(asObject(jsValue)); + Document* document = jsDocument->impl(); + RefPtr<NodeList> nodes = document->nodesFromRect(x, y, top, right, bottom, left, ignoreClipping); + return toRef(exec, toJS(exec, jsDocument->globalObject(), nodes.get())); +} + +@end + +#ifdef BUILDING_ON_LEOPARD + +static IMP originalRecursivelyRemoveMailAttributesImp; + +static id objectElementDataAttribute(DOMHTMLObjectElement *self, SEL) +{ + return [self getAttribute:@"data"]; +} + +static void recursivelyRemoveMailAttributes(DOMNode *self, SEL selector, BOOL a, BOOL b, BOOL c) +{ + // While inside this Mail function, change the behavior of -[DOMHTMLObjectElement data] back to what it used to be + // before we fixed a bug in it (see http://trac.webkit.org/changeset/30044 for that change). + + // It's a little bit strange to patch a method defined by WebKit, but it helps keep this workaround self-contained. + + Method methodToPatch = class_getInstanceMethod(objc_getRequiredClass("DOMHTMLObjectElement"), @selector(data)); + IMP originalDataImp = method_setImplementation(methodToPatch, reinterpret_cast<IMP>(objectElementDataAttribute)); + originalRecursivelyRemoveMailAttributesImp(self, selector, a, b, c); + method_setImplementation(methodToPatch, originalDataImp); +} + +#endif + +static void patchMailRemoveAttributesMethod() +{ +#ifdef BUILDING_ON_LEOPARD + if (!WKAppVersionCheckLessThan(@"com.apple.mail", -1, 4.0)) + return; + Method methodToPatch = class_getInstanceMethod(objc_getRequiredClass("DOMNode"), @selector(recursivelyRemoveMailAttributes:convertObjectsToImages:convertEditableElements:)); + if (!methodToPatch) + return; + originalRecursivelyRemoveMailAttributesImp = method_setImplementation(methodToPatch, reinterpret_cast<IMP>(recursivelyRemoveMailAttributes)); +#endif +} diff --git a/Source/WebKit/mac/WebView/WebViewData.h b/Source/WebKit/mac/WebView/WebViewData.h new file mode 100644 index 0000000..97d0fc3 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebViewData.h @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Igalia S.L + * + * 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 "WebTypesInternal.h" +#import "WebDelegateImplementationCaching.h" +#import <WebCore/PlatformString.h> +#import <WebCore/WebCoreKeyboardUIMode.h> +#import <wtf/HashMap.h> +#import <wtf/RetainPtr.h> + +namespace WebCore { + class Page; +} + +@class WebInspector; +@class WebNodeHighlight; +@class WebPluginDatabase; +@class WebPreferences; +@class WebTextCompletionController; +@protocol WebFormDelegate; +@protocol WebDeviceOrientationProvider; +@protocol WebGeolocationProvider; +#if ENABLE(VIDEO) +@class WebVideoFullscreenController; +#endif +#if ENABLE(FULLSCREEN_API) +@class WebFullScreenController; +#endif + +extern BOOL applicationIsTerminating; +extern int pluginDatabaseClientCount; + +// FIXME: This should be renamed to WebViewData. +@interface WebViewPrivate : NSObject { +@public + WebCore::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 historyDelegate; + + WebInspector *inspector; + WebNodeHighlight *currentNodeHighlight; + + BOOL allowsUndo; + + float zoomMultiplier; + BOOL zoomsTextOnly; + + NSString *applicationNameForUserAgent; + WTF::String userAgent; + BOOL userAgentOverridden; + + WebPreferences *preferences; + BOOL useSiteSpecificSpoofing; + + NSWindow *hostWindow; + + int programmaticFocusCount; + + WebResourceDelegateImplementationCache resourceLoadDelegateImplementations; + WebFrameLoadDelegateImplementationCache frameLoadDelegateImplementations; + WebScriptDebugDelegateImplementationCache scriptDebugDelegateImplementations; + WebHistoryDelegateImplementationCache historyDelegateImplementations; + + void *observationInfo; + + BOOL closed; + BOOL shouldCloseWithWindow; + BOOL mainFrameDocumentReady; + BOOL drawsBackground; + BOOL editable; + BOOL tabKeyCyclesThroughElementsChanged; + BOOL becomingFirstResponder; + BOOL becomingFirstResponderFromOutside; + BOOL hoverFeedbackSuspended; + BOOL usesPageCache; + BOOL catchesDelegateExceptions; + BOOL cssAnimationsSuspended; + + NSColor *backgroundColor; + + NSString *mediaStyle; + + BOOL hasSpellCheckerDocumentTag; + NSInteger spellCheckerDocumentTag; + + BOOL smartInsertDeleteEnabled; + BOOL selectTrailingWhitespaceEnabled; + +#if ENABLE(DASHBOARD_SUPPORT) + BOOL dashboardBehaviorAlwaysSendMouseEventsToAllWindows; + BOOL dashboardBehaviorAlwaysSendActiveNullEventsToPlugIns; + BOOL dashboardBehaviorAlwaysAcceptsFirstMouse; + BOOL dashboardBehaviorAllowWheelScrolling; +#endif + + // 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; + + BOOL _keyboardUIModeAccessed; + WebCore::KeyboardUIMode _keyboardUIMode; + + BOOL shouldUpdateWhileOffscreen; + + // When this flag is unset, we will not make any subviews underneath this WebView. This means no WebFrameViews and no WebHTMLViews. + BOOL usesDocumentViews; + + BOOL includesFlattenedCompositingLayersWhenDrawingToBitmap; + +#if USE(ACCELERATED_COMPOSITING) + // When this flag is set, next time a WebHTMLView draws, it needs to temporarily disable screen updates + // so that the NSView drawing is visually synchronized with CALayer updates. + BOOL needsOneShotDrawingSynchronization; + BOOL postsAcceleratedCompositingNotifications; + // Run loop observer used to implement the compositing equivalent of -viewWillDraw + CFRunLoopObserverRef layerSyncRunLoopObserver; +#endif + + NSPasteboard *insertionPasteboard; + + NSSize lastLayoutSize; + + BOOL ignoringMouseDraggedEvents; + + NSEvent *mouseDownEvent; // Kept after handling the event. + BOOL handlingMouseDownEvent; + NSEvent *keyDownEvent; // Kept after handling the event. + + WebTextCompletionController *completionController; + + NSTimer *autoscrollTimer; + NSEvent *autoscrollTriggerEvent; + + CFRunLoopTimerRef updateMouseoverTimer; +#if ENABLE(VIDEO) + WebVideoFullscreenController *fullscreenController; +#endif + +#if ENABLE(FULLSCREEN_API) + WebFullScreenController *newFullscreenController; +#endif + +#if ENABLE(GLIB_SUPPORT) + CFRunLoopObserverRef glibRunLoopObserver; +#endif + id<WebGeolocationProvider> _geolocationProvider; + id<WebDeviceOrientationProvider> m_deviceOrientationProvider; +} +@end diff --git a/Source/WebKit/mac/WebView/WebViewData.mm b/Source/WebKit/mac/WebView/WebViewData.mm new file mode 100644 index 0000000..9794d03 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebViewData.mm @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009 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 "WebViewData.h" + +#import "WebKitLogging.h" +#import "WebPreferenceKeysPrivate.h" +#import <WebCore/WebCoreObjCExtras.h> +#import <objc/objc-auto.h> +#import <runtime/InitializeThreading.h> +#import <wtf/Threading.h> + +BOOL applicationIsTerminating = NO; +int pluginDatabaseClientCount = 0; + +@implementation WebViewPrivate + ++ (void)initialize +{ + JSC::initializeThreading(); + WTF::initializeMainThreadToProcessMainThread(); +#ifndef BUILDING_ON_TIGER + WebCoreObjCFinalizeOnMainThread(self); +#endif +} + +- (id)init +{ + self = [super init]; + if (!self) + return nil; + + allowsUndo = YES; + usesPageCache = YES; + shouldUpdateWhileOffscreen = YES; + cssAnimationsSuspended = NO; + + zoomMultiplier = 1; + zoomsTextOnly = NO; + +#if ENABLE(DASHBOARD_SUPPORT) + dashboardBehaviorAllowWheelScrolling = YES; +#endif + +#if !defined(BUILDING_ON_TIGER) + shouldCloseWithWindow = objc_collectingEnabled(); +#else + shouldCloseWithWindow = NO; +#endif + + smartInsertDeleteEnabled = ![[NSUserDefaults standardUserDefaults] objectForKey:WebSmartInsertDeleteEnabled] + || [[NSUserDefaults standardUserDefaults] boolForKey:WebSmartInsertDeleteEnabled]; + + + pluginDatabaseClientCount++; + + return self; +} + +- (void)dealloc +{ + ASSERT(applicationIsTerminating || !page); + ASSERT(applicationIsTerminating || !preferences); + ASSERT(!insertionPasteboard); +#if ENABLE(VIDEO) + ASSERT(!fullscreenController); +#endif + + [applicationNameForUserAgent release]; + [backgroundColor release]; + [inspector release]; + [currentNodeHighlight release]; + [hostWindow release]; + [policyDelegateForwarder release]; + [UIDelegateForwarder release]; + [frameLoadDelegateForwarder release]; + [editingDelegateForwarder release]; + [mediaStyle release]; + + [super dealloc]; +} + +- (void)finalize +{ + ASSERT_MAIN_THREAD(); + ASSERT(!insertionPasteboard); +#if ENABLE(VIDEO) + ASSERT(!fullscreenController); +#endif + + [super finalize]; +} + +@end diff --git a/Source/WebKit/mac/WebView/WebViewEventHandling.mm b/Source/WebKit/mac/WebView/WebViewEventHandling.mm new file mode 100644 index 0000000..a185667 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebViewEventHandling.mm @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009 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 "WebFrameInternal.h" +#import "WebHTMLView.h" +#import "WebTextCompletionController.h" +#import "WebViewData.h" +#import <WebCore/Frame.h> + +using namespace WebCore; + +@class NSTextInputContext; + +@interface NSResponder (WebNSResponderDetails) +- (NSTextInputContext *)inputContext; +@end + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) +@interface NSObject (NSTextInputContextDetails) +- (BOOL)wantsToHandleMouseEvents; +- (BOOL)handleMouseEvent:(NSEvent *)event; +@end +#endif + +@implementation WebView (WebViewEventHandling) + +static WebView *lastMouseoverView; + +- (void)_closingEventHandling +{ + if (lastMouseoverView == self) + lastMouseoverView = nil; +} + +- (void)_setMouseDownEvent:(NSEvent *)event +{ + ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown); + + if (event == _private->mouseDownEvent) + return; + + [event retain]; + [_private->mouseDownEvent release]; + _private->mouseDownEvent = event; +} + +- (void)mouseDown:(NSEvent *)event +{ + // FIXME (Viewless): This method should be shared with WebHTMLView, which needs to + // do the same work in the usesDocumentViews case. We don't want to maintain two + // duplicate copies of this method. + + if (_private->usesDocumentViews) { + [super mouseDown:event]; + return; + } + + // There's a chance that responding to this event will run a nested event loop, and + // fetching a new event might release the old one. Retaining and then autoreleasing + // the current event prevents that from causing a problem inside WebKit or AppKit code. + [[event retain] autorelease]; + + RetainPtr<WebView> protector = self; + if ([[self inputContext] wantsToHandleMouseEvents] && [[self inputContext] handleMouseEvent:event]) + return; + + _private->handlingMouseDownEvent = YES; + + // Record the mouse down position so we can determine drag hysteresis. + [self _setMouseDownEvent:event]; + + NSInputManager *currentInputManager = [NSInputManager currentInputManager]; + if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event]) + goto done; + + [_private->completionController endRevertingChange:NO moveLeft:NO]; + + // If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here. + // We don't want to pass them along to KHTML a second time. + if (!([event modifierFlags] & NSControlKeyMask)) { + _private->ignoringMouseDraggedEvents = NO; + + // Don't do any mouseover while the mouse is down. + [self _cancelUpdateMouseoverTimer]; + + // Let WebCore get a chance to deal with the event. This will call back to us + // to start the autoscroll timer if appropriate. + if (Frame* frame = [self _mainCoreFrame]) + frame->eventHandler()->mouseDown(event); + } + +done: + _private->handlingMouseDownEvent = NO; +} + +- (void)mouseUp:(NSEvent *)event +{ + // FIXME (Viewless): This method should be shared with WebHTMLView, which needs to + // do the same work in the usesDocumentViews case. We don't want to maintain two + // duplicate copies of this method. + + if (_private->usesDocumentViews) { + [super mouseUp:event]; + return; + } + + // There's a chance that responding to this event will run a nested event loop, and + // fetching a new event might release the old one. Retaining and then autoreleasing + // the current event prevents that from causing a problem inside WebKit or AppKit code. + [[event retain] autorelease]; + + [self _setMouseDownEvent:nil]; + + NSInputManager *currentInputManager = [NSInputManager currentInputManager]; + if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event]) + return; + + [self retain]; + + [self _stopAutoscrollTimer]; + if (Frame* frame = [self _mainCoreFrame]) + frame->eventHandler()->mouseUp(event); + [self _updateMouseoverWithFakeEvent]; + + [self release]; +} + ++ (void)_updateMouseoverWithEvent:(NSEvent *)event +{ + WebView *oldView = lastMouseoverView; + + lastMouseoverView = nil; + + NSView *contentView = [[event window] contentView]; + NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil]; + for (NSView *hitView = [contentView hitTest:locationForHitTest]; hitView; hitView = [hitView superview]) { + if ([hitView isKindOfClass:[WebView class]]) { + lastMouseoverView = static_cast<WebView *>(hitView); + break; + } + } + + if (lastMouseoverView && lastMouseoverView->_private->hoverFeedbackSuspended) + lastMouseoverView = nil; + + if (lastMouseoverView != oldView) { + if (Frame* oldCoreFrame = [oldView _mainCoreFrame]) { + NSEvent *oldViewEvent = [NSEvent mouseEventWithType:NSMouseMoved + location:NSMakePoint(-1, -1) + modifierFlags:[[NSApp currentEvent] modifierFlags] + timestamp:[NSDate timeIntervalSinceReferenceDate] + windowNumber:[[oldView window] windowNumber] + context:[[NSApp currentEvent] context] + eventNumber:0 clickCount:0 pressure:0]; + oldCoreFrame->eventHandler()->mouseMoved(oldViewEvent); + } + } + + if (!lastMouseoverView) + return; + + if (Frame* coreFrame = core([lastMouseoverView mainFrame])) + coreFrame->eventHandler()->mouseMoved(event); +} + +- (void)_updateMouseoverWithFakeEvent +{ + [self _cancelUpdateMouseoverTimer]; + + NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved + location:[[self window] convertScreenToBase:[NSEvent mouseLocation]] + modifierFlags:[[NSApp currentEvent] modifierFlags] + timestamp:[NSDate timeIntervalSinceReferenceDate] + windowNumber:[[self window] windowNumber] + context:[[NSApp currentEvent] context] + eventNumber:0 clickCount:0 pressure:0]; + + [[self class] _updateMouseoverWithEvent:fakeEvent]; +} + +- (void)_cancelUpdateMouseoverTimer +{ + if (_private->updateMouseoverTimer) { + CFRunLoopTimerInvalidate(_private->updateMouseoverTimer); + CFRelease(_private->updateMouseoverTimer); + _private->updateMouseoverTimer = NULL; + } +} + +- (void)_stopAutoscrollTimer +{ + NSTimer *timer = _private->autoscrollTimer; + _private->autoscrollTimer = nil; + [_private->autoscrollTriggerEvent release]; + _private->autoscrollTriggerEvent = nil; + [timer invalidate]; + [timer release]; +} + +- (void)_setToolTip:(NSString *)toolTip +{ + if (_private->usesDocumentViews) { + id documentView = [[[self _selectedOrMainFrame] frameView] documentView]; + if ([documentView isKindOfClass:[WebHTMLView class]]) + [documentView _setToolTip:toolTip]; + return; + } + + // FIXME (Viewless): Code to handle tooltips needs to move into WebView. +} + +@end diff --git a/Source/WebKit/mac/WebView/WebViewInternal.h b/Source/WebKit/mac/WebView/WebViewInternal.h new file mode 100644 index 0000000..555c4d1 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebViewInternal.h @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Igalia S.L + * + * 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. + */ + +// This header contains WebView declarations that can be used anywhere in WebKit, but are neither SPI nor API. + +#import "WebPreferences.h" +#import "WebViewPrivate.h" +#import "WebTypesInternal.h" + +#ifdef __cplusplus +#import <WebCore/WebCoreKeyboardUIMode.h> + +#include <wtf/Forward.h> + +namespace WebCore { + class Element; + class Frame; + class KURL; + class KeyboardEvent; + class Page; + class RenderBox; + class Node; +} +#endif + +@class WebBasePluginPackage; +@class WebDownload; +@class WebNodeHighlight; + +#ifdef __cplusplus + +@interface WebView (WebViewEditingExtras) +- (BOOL)_interceptEditingKeyEvent:(WebCore::KeyboardEvent*)event shouldSaveCommand:(BOOL)shouldSave; +- (BOOL)_shouldChangeSelectedDOMRange:(DOMRange *)currentRange toDOMRange:(DOMRange *)proposedRange affinity:(NSSelectionAffinity)selectionAffinity stillSelecting:(BOOL)flag; +@end + +@interface WebView (AllWebViews) ++ (void)_makeAllWebViewsPerformSelector:(SEL)selector; +- (void)_removeFromAllWebViewsSet; +- (void)_addToAllWebViewsSet; +@end + +@interface WebView (WebViewInternal) + ++ (BOOL)shouldIncludeInWebKitStatistics; + +- (WebCore::Frame*)_mainCoreFrame; +- (WebFrame *)_selectedOrMainFrame; + +- (WebCore::KeyboardUIMode)_keyboardUIMode; + +- (BOOL)_becomingFirstResponderFromOutside; + +#if ENABLE(ICONDATABASE) +- (void)_registerForIconNotification:(BOOL)listen; +- (void)_dispatchDidReceiveIconFromWebFrame:(WebFrame *)webFrame; +#endif + +- (void)_selectionChanged; + +#if USE(ACCELERATED_COMPOSITING) +- (BOOL)_needsOneShotDrawingSynchronization; +- (void)_setNeedsOneShotDrawingSynchronization:(BOOL)needsSynchronization; +- (void)_scheduleCompositingLayerSync; +#endif + +#if ENABLE(GLIB_SUPPORT) +- (void)_scheduleGlibContextIterations; +#endif + +@end + +#endif + +@interface WebView (WebViewEventHandling) +- (void)_closingEventHandling; +- (void)_updateMouseoverWithFakeEvent; +- (void)_cancelUpdateMouseoverTimer; +- (void)_stopAutoscrollTimer; +- (void)_setToolTip:(NSString *)toolTip; +@end + +// FIXME: Temporary way to expose methods that are in the wrong category inside WebView. +@interface WebView (WebViewOtherInternal) + ++ (void)_setCacheModel:(WebCacheModel)cacheModel; ++ (WebCacheModel)_cacheModel; + +#ifdef __cplusplus +- (WebCore::Page*)page; +#endif + +- (NSMenu *)_menuForElement:(NSDictionary *)element defaultItems:(NSArray *)items; +- (id)_UIDelegateForwarder; +- (id)_editingDelegateForwarder; +- (id)_policyDelegateForwarder; +- (void)_pushPerformingProgrammaticFocus; +- (void)_popPerformingProgrammaticFocus; +- (void)_didStartProvisionalLoadForFrame:(WebFrame *)frame; ++ (BOOL)_viewClass:(Class *)vClass andRepresentationClass:(Class *)rClass forMIMEType:(NSString *)MIMEType allowingPlugins:(BOOL)allowPlugins; +- (BOOL)_viewClass:(Class *)vClass andRepresentationClass:(Class *)rClass forMIMEType:(NSString *)MIMEType; ++ (void)_registerPluginMIMEType:(NSString *)MIMEType; ++ (void)_unregisterPluginMIMEType:(NSString *)MIMEType; ++ (BOOL)_canShowMIMEType:(NSString *)MIMEType allowingPlugins:(BOOL)allowPlugins; +- (BOOL)_canShowMIMEType:(NSString *)MIMEType; ++ (NSString *)_MIMETypeForFile:(NSString *)path; +- (WebDownload *)_downloadURL:(NSURL *)URL; ++ (NSString *)_generatedMIMETypeForURLScheme:(NSString *)URLScheme; ++ (BOOL)_representationExistsForURLScheme:(NSString *)URLScheme; +- (BOOL)_isPerformingProgrammaticFocus; +- (void)_mouseDidMoveOverElement:(NSDictionary *)dictionary modifierFlags:(NSUInteger)modifierFlags; +- (WebView *)_openNewWindowWithRequest:(NSURLRequest *)request; +- (void)_writeImageForElement:(NSDictionary *)element withPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard; +- (void)_writeLinkElement:(NSDictionary *)element withPasteboardTypes:(NSArray *)types toPasteboard:(NSPasteboard *)pasteboard; +- (void)_openFrameInNewWindowFromMenu:(NSMenuItem *)sender; +- (void)_searchWithGoogleFromMenu:(id)sender; +- (void)_searchWithSpotlightFromMenu:(id)sender; +- (void)_progressCompleted:(WebFrame *)frame; +- (void)_didCommitLoadForFrame:(WebFrame *)frame; +- (void)_didFinishLoadForFrame:(WebFrame *)frame; +- (void)_didFailLoadWithError:(NSError *)error forFrame:(WebFrame *)frame; +- (void)_didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame; +- (void)_willChangeValueForKey:(NSString *)key; +- (void)_didChangeValueForKey:(NSString *)key; +- (WebBasePluginPackage *)_pluginForMIMEType:(NSString *)MIMEType; +- (WebBasePluginPackage *)_pluginForExtension:(NSString *)extension; +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +- (WebBasePluginPackage *)_videoProxyPluginForMIMEType:(NSString *)MIMEType; +#endif + +- (void)setCurrentNodeHighlight:(WebNodeHighlight *)nodeHighlight; +- (WebNodeHighlight *)currentNodeHighlight; + +- (void)addPluginInstanceView:(NSView *)view; +- (void)removePluginInstanceView:(NSView *)view; +- (void)removePluginInstanceViewsFor:(WebFrame*)webFrame; + +- (void)_addObject:(id)object forIdentifier:(unsigned long)identifier; +- (id)_objectForIdentifier:(unsigned long)identifier; +- (void)_removeObjectForIdentifier:(unsigned long)identifier; + +- (void)_setZoomMultiplier:(float)multiplier isTextOnly:(BOOL)isTextOnly; +- (float)_zoomMultiplier:(BOOL)isTextOnly; +- (float)_realZoomMultiplier; +- (BOOL)_realZoomMultiplierIsTextOnly; +- (BOOL)_canZoomOut:(BOOL)isTextOnly; +- (BOOL)_canZoomIn:(BOOL)isTextOnly; +- (IBAction)_zoomOut:(id)sender isTextOnly:(BOOL)isTextOnly; +- (IBAction)_zoomIn:(id)sender isTextOnly:(BOOL)isTextOnly; +- (BOOL)_canResetZoom:(BOOL)isTextOnly; +- (IBAction)_resetZoom:(id)sender isTextOnly:(BOOL)isTextOnly; + +- (BOOL)_mustDrawUnionedRect:(NSRect)rect singleRects:(const NSRect *)rects count:(NSInteger)count; + ++ (BOOL)_canHandleRequest:(NSURLRequest *)request forMainFrame:(BOOL)forMainFrame; + +- (void)_setInsertionPasteboard:(NSPasteboard *)pasteboard; + +#if ENABLE(VIDEO) && defined(__cplusplus) +- (void)_enterFullscreenForNode:(WebCore::Node*)node; +- (void)_exitFullscreen; +#endif + +#if ENABLE(FULLSCREEN_API) && defined(__cplusplus) +- (BOOL)_supportsFullScreenForElement:(WebCore::Element*)element; +- (void)_enterFullScreenForElement:(WebCore::Element*)element; +- (void)_exitFullScreenForElement:(WebCore::Element*)element; +- (void)_fullScreenRendererChanged:(WebCore::RenderBox*)renderer; +#endif + +- (JSValueRef)_computedStyleIncludingVisitedInfo:(JSContextRef)context forElement:(JSValueRef)value; + +@end diff --git a/Source/WebKit/mac/WebView/WebViewPrivate.h b/Source/WebKit/mac/WebView/WebViewPrivate.h new file mode 100644 index 0000000..a9ef1cb --- /dev/null +++ b/Source/WebKit/mac/WebView/WebViewPrivate.h @@ -0,0 +1,691 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * 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 <WebKit/WebView.h> +#import <WebKit/WebFramePrivate.h> +#import <JavaScriptCore/JSBase.h> + +#if !defined(ENABLE_DASHBOARD_SUPPORT) +#define ENABLE_DASHBOARD_SUPPORT 1 +#endif + +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 +#define WebNSInteger int +#define WebNSUInteger unsigned int +#else +#define WebNSInteger NSInteger +#define WebNSUInteger NSUInteger +#endif + +@class NSError; +@class WebFrame; +@class WebDeviceOrientation; +@class WebGeolocationPosition; +@class WebInspector; +@class WebPreferences; +@class WebScriptWorld; +@class WebTextIterator; + +@protocol WebDeviceOrientationProvider; +@protocol WebFormDelegate; + +extern NSString *_WebCanGoBackKey; +extern NSString *_WebCanGoForwardKey; +extern NSString *_WebEstimatedProgressKey; +extern NSString *_WebIsLoadingKey; +extern NSString *_WebMainFrameIconKey; +extern NSString *_WebMainFrameTitleKey; +extern NSString *_WebMainFrameURLKey; +extern NSString *_WebMainFrameDocumentKey; + +// pending public WebElementDictionary keys +extern NSString *WebElementTitleKey; // NSString of the title of the element (used by Safari) +extern NSString *WebElementSpellingToolTipKey; // NSString of a tooltip representing misspelling or bad grammar (used internally) +extern NSString *WebElementIsContentEditableKey; // NSNumber indicating whether the inner non-shared node is content editable (used internally) +extern NSString *WebElementMediaURLKey; // NSURL of the media element + +// other WebElementDictionary keys +extern NSString *WebElementLinkIsLiveKey; // NSNumber of BOOL indicating whether the link is live or not +extern NSString *WebElementIsInScrollBarKey; + +// One of the subviews of the WebView entered compositing mode. +extern NSString *_WebViewDidStartAcceleratedCompositingNotification; + +#if ENABLE_DASHBOARD_SUPPORT +typedef enum { + WebDashboardBehaviorAlwaysSendMouseEventsToAllWindows, + WebDashboardBehaviorAlwaysSendActiveNullEventsToPlugIns, + WebDashboardBehaviorAlwaysAcceptsFirstMouse, + WebDashboardBehaviorAllowWheelScrolling, + WebDashboardBehaviorUseBackwardCompatibilityMode +} WebDashboardBehavior; +#endif + +typedef enum { + WebInjectAtDocumentStart, + WebInjectAtDocumentEnd, +} WebUserScriptInjectionTime; + +typedef enum { + WebInjectInAllFrames, + WebInjectInTopFrameOnly +} WebUserContentInjectedFrames; + +enum { + WebFindOptionsCaseInsensitive = 1 << 0, + WebFindOptionsAtWordStarts = 1 << 1, + WebFindOptionsTreatMedialCapitalAsWordStart = 1 << 2, + WebFindOptionsBackwards = 1 << 3, + WebFindOptionsWrapAround = 1 << 4, + WebFindOptionsStartInSelection = 1 << 5 +}; +typedef NSUInteger WebFindOptions; + +@interface WebController : NSTreeController { + IBOutlet WebView *webView; +} +- (WebView *)webView; +- (void)setWebView:(WebView *)newWebView; +@end + +@interface WebView (WebViewEditingActionsPendingPublic) + +- (void)outdent:(id)sender; + +@end + +@interface WebView (WebPendingPublic) + +- (void)scheduleInRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode; +- (void)unscheduleFromRunLoop:(NSRunLoop *)runLoop forMode:(NSString *)mode; + +- (BOOL)findString:(NSString *)string options:(WebFindOptions)options; + +- (void)setMainFrameDocumentReady:(BOOL)mainFrameDocumentReady; + +- (void)setTabKeyCyclesThroughElements:(BOOL)cyclesElements; +- (BOOL)tabKeyCyclesThroughElements; + +- (void)scrollDOMRangeToVisible:(DOMRange *)range; + +// setHoverFeedbackSuspended: can be called by clients that want to temporarily prevent the webView +// from displaying feedback about mouse position. Each WebDocumentView class that displays feedback +// about mouse position should honor this setting. +- (void)setHoverFeedbackSuspended:(BOOL)newValue; +- (BOOL)isHoverFeedbackSuspended; + +/*! +@method setScriptDebugDelegate: +@abstract Set the WebView's WebScriptDebugDelegate delegate. +@param delegate The WebScriptDebugDelegate to set as the delegate. +*/ +- (void)setScriptDebugDelegate:(id)delegate; + +/*! +@method scriptDebugDelegate +@abstract Return the WebView's WebScriptDebugDelegate. +@result The WebView's WebScriptDebugDelegate. +*/ +- (id)scriptDebugDelegate; + +/*! + @method setHistoryDelegate: + @abstract Set the WebView's WebHistoryDelegate delegate. + @param delegate The WebHistoryDelegate to set as the delegate. +*/ +- (void)setHistoryDelegate:(id)delegate; + +/*! + @method historyDelegate + @abstract Return the WebView's WebHistoryDelegate delegate. + @result The WebView's WebHistoryDelegate delegate. +*/ +- (id)historyDelegate; + +- (BOOL)shouldClose; + +/*! + @method aeDescByEvaluatingJavaScriptFromString: + @param script The text of the JavaScript. + @result The result of the script, converted to an NSAppleEventDescriptor, or nil for failure. +*/ +- (NSAppleEventDescriptor *)aeDescByEvaluatingJavaScriptFromString:(NSString *)script; + +// Support for displaying multiple text matches. +// These methods might end up moving into a protocol, so different document types can specify +// whether or not they implement the protocol. For now we'll just deal with HTML. +// These methods are still in flux; don't rely on them yet. +- (BOOL)canMarkAllTextMatches; +- (WebNSUInteger)countMatchesForText:(NSString *)string options:(WebFindOptions)options highlight:(BOOL)highlight limit:(WebNSUInteger)limit markMatches:(BOOL)markMatches; +- (WebNSUInteger)countMatchesForText:(NSString *)string inDOMRange:(DOMRange *)range options:(WebFindOptions)options highlight:(BOOL)highlight limit:(WebNSUInteger)limit markMatches:(BOOL)markMatches; +- (void)unmarkAllTextMatches; +- (NSArray *)rectsForTextMatches; + +// Support for disabling registration with the undo manager. This is equivalent to the methods with the same names on NSTextView. +- (BOOL)allowsUndo; +- (void)setAllowsUndo:(BOOL)flag; + +/*! + @method setPageSizeMultiplier: + @abstract Change the zoom factor of the page in views managed by this webView. + @param multiplier A fractional percentage value, 1.0 is 100%. +*/ +- (void)setPageSizeMultiplier:(float)multiplier; + +/*! + @method pageSizeMultiplier + @result The page size multipler. +*/ +- (float)pageSizeMultiplier; + +// Commands for doing page zoom. Will end up in WebView (WebIBActions) <NSUserInterfaceValidations> +- (BOOL)canZoomPageIn; +- (IBAction)zoomPageIn:(id)sender; +- (BOOL)canZoomPageOut; +- (IBAction)zoomPageOut:(id)sender; +- (BOOL)canResetPageZoom; +- (IBAction)resetPageZoom:(id)sender; + +// Sets a master volume control for all media elements in the WebView. Valid values are 0..1. +- (void)setMediaVolume:(float)volume; +- (float)mediaVolume; + +// Add visited links +- (void)addVisitedLinks:(NSArray *)visitedLinks; + +@end + +@interface WebView (WebPrivate) + +- (WebInspector *)inspector; + +/*! + @method setBackgroundColor: + @param backgroundColor Color to use as the default background. + @abstract Sets what color the receiver draws under transparent page background colors and images. + This color is also used when no page is loaded. A color with alpha should only be used when the receiver is + in a non-opaque window, since the color is drawn using NSCompositeCopy. +*/ +- (void)setBackgroundColor:(NSColor *)backgroundColor; + +/*! + @method backgroundColor + @result Returns the background color drawn under transparent page background colors and images. + This color is also used when no page is loaded. A color with alpha should only be used when the receiver is + in a non-opaque window, since the color is drawn using NSCompositeCopy. +*/ +- (NSColor *)backgroundColor; + +/*! +Could be worth adding to the API. + @method _loadBackForwardListFromOtherView: + @abstract Loads the view with the contents of the other view, including its backforward list. + @param otherView The WebView from which to copy contents. + */ +- (void)_loadBackForwardListFromOtherView:(WebView *)otherView; + +/* + @method _reportException:inContext: + @abstract Logs the exception to the Web Inspector. This only needs called for exceptions that + occur while using the JavaScriptCore APIs with a context owned by a WebKit. + @param exception The exception value to log. + @param context The context the exception occured in. +*/ ++ (void)_reportException:(JSValueRef)exception inContext:(JSContextRef)context; + +/*! + @method _dispatchPendingLoadRequests: + @abstract Dispatches any pending load requests that have been scheduled because of recent DOM additions or style changes. + @discussion You only need to call this method if you require synchronous notification of loads through the resource load delegate. + Otherwise the resource load delegate will be notified about loads during a future run loop iteration. + */ +- (void)_dispatchPendingLoadRequests; + ++ (NSArray *)_supportedFileExtensions; + +/*! + @method canShowFile: + @abstract Checks if the WebKit can show the content of the file at the specified path. + @param path The path of the file to check + @result YES if the WebKit can show the content of the file at the specified path. +*/ ++ (BOOL)canShowFile:(NSString *)path; + +/*! + @method suggestedFileExtensionForMIMEType: + @param MIMEType The MIME type to check. + @result The extension based on the MIME type +*/ ++ (NSString *)suggestedFileExtensionForMIMEType: (NSString *)MIMEType; + ++ (NSString *)_standardUserAgentWithApplicationName:(NSString *)applicationName; + +/*! + @method canCloseAllWebViews + @abstract Checks if all the open WebViews can be closed (by dispatching the beforeUnload event to the pages). + @result YES if all the WebViews can be closed. +*/ ++ (BOOL)canCloseAllWebViews; + +// May well become public +- (void)_setFormDelegate:(id<WebFormDelegate>)delegate; +- (id<WebFormDelegate>)_formDelegate; + +- (BOOL)_isClosed; + +// _close is now replaced by public method -close. It remains here only for backward compatibility +// until callers can be weaned off of it. +- (void)_close; + +// Indicates if the WebView is in the midst of a user gesture. +- (BOOL)_isProcessingUserGesture; + +// SPI for DumpRenderTree +- (void)_updateActiveState; + +/*! + @method _registerViewClass:representationClass:forURLScheme: + @discussion Register classes that implement WebDocumentView and WebDocumentRepresentation respectively. + @param viewClass The WebDocumentView class to use to render data for a given MIME type. + @param representationClass The WebDocumentRepresentation class to use to represent data of the given MIME type. + @param scheme The URL scheme to represent with an object of the given class. +*/ ++ (void)_registerViewClass:(Class)viewClass representationClass:(Class)representationClass forURLScheme:(NSString *)URLScheme; + ++ (void)_unregisterViewClassAndRepresentationClassForMIMEType:(NSString *)MIMEType; + +/*! + @method _canHandleRequest: + @abstract Performs a "preflight" operation that performs some + speculative checks to see if a request can be used to create + a WebDocumentView and WebDocumentRepresentation. + @discussion The result of this method is valid only as long as no + protocols or schemes are registered or unregistered, and as long as + the request is not mutated (if the request is mutable). Hence, clients + should be prepared to handle failures even if they have performed request + preflighting by caling this method. + @param request The request to preflight. + @result YES if it is likely that a WebDocumentView and WebDocumentRepresentation + can be created for the request, NO otherwise. +*/ ++ (BOOL)_canHandleRequest:(NSURLRequest *)request; + ++ (NSString *)_decodeData:(NSData *)data; + ++ (void)_setAlwaysUsesComplexTextCodePath:(BOOL)f; +// This is the old name of the above method. Needed for Safari versions that call it. ++ (void)_setAlwaysUseATSU:(BOOL)f; + +- (NSCachedURLResponse *)_cachedResponseForURL:(NSURL *)URL; + +#if ENABLE_DASHBOARD_SUPPORT +- (void)_addScrollerDashboardRegions:(NSMutableDictionary *)regions; +- (NSDictionary *)_dashboardRegions; + +- (void)_setDashboardBehavior:(WebDashboardBehavior)behavior to:(BOOL)flag; +- (BOOL)_dashboardBehavior:(WebDashboardBehavior)behavior; +#endif + ++ (void)_setShouldUseFontSmoothing:(BOOL)f; ++ (BOOL)_shouldUseFontSmoothing; + +- (void)_setCatchesDelegateExceptions:(BOOL)f; +- (BOOL)_catchesDelegateExceptions; + +// These two methods are useful for a test harness that needs a consistent appearance for the focus rings +// regardless of OS X version. ++ (void)_setUsesTestModeFocusRingColor:(BOOL)f; ++ (BOOL)_usesTestModeFocusRingColor; + +/*! + @method setAlwaysShowVerticalScroller: + @result Forces the vertical scroller to be visible if flag is YES, otherwise + if flag is NO the scroller with automatically show and hide as needed. + */ +- (void)setAlwaysShowVerticalScroller:(BOOL)flag; + +/*! + @method alwaysShowVerticalScroller + @result YES if the vertical scroller is always shown + */ +- (BOOL)alwaysShowVerticalScroller; + +/*! + @method setAlwaysShowHorizontalScroller: + @result Forces the horizontal scroller to be visible if flag is YES, otherwise + if flag is NO the scroller with automatically show and hide as needed. + */ +- (void)setAlwaysShowHorizontalScroller:(BOOL)flag; + +/*! + @method alwaysShowHorizontalScroller + @result YES if the horizontal scroller is always shown + */ +- (BOOL)alwaysShowHorizontalScroller; + +/*! + @method setProhibitsMainFrameScrolling: + @abstract Prohibits scrolling in the WebView's main frame. Used to "lock" a WebView + to a specific scroll position. + */ +- (void)setProhibitsMainFrameScrolling:(BOOL)prohibits; + +/*! + @method _setAdditionalWebPlugInPaths: + @abstract Sets additional plugin search paths for a specific WebView. + */ +- (void)_setAdditionalWebPlugInPaths:(NSArray *)newPaths; + +/*! + @method _setInViewSourceMode: + @abstract Used to place a WebView into a special source-viewing mode. + */ +- (void)_setInViewSourceMode:(BOOL)flag; + +/*! + @method _inViewSourceMode; + @abstract Whether or not the WebView is in source-view mode for HTML. + */ +- (BOOL)_inViewSourceMode; + +/*! + @method _attachScriptDebuggerToAllFrames + @abstract Attaches a script debugger to all frames belonging to the receiver. + */ +- (void)_attachScriptDebuggerToAllFrames; + +/*! + @method _detachScriptDebuggerFromAllFrames + @abstract Detaches any script debuggers from all frames belonging to the receiver. + */ +- (void)_detachScriptDebuggerFromAllFrames; + +- (BOOL)defersCallbacks; // called by QuickTime plug-in +- (void)setDefersCallbacks:(BOOL)defer; // called by QuickTime plug-in + +- (BOOL)usesPageCache; +- (void)setUsesPageCache:(BOOL)usesPageCache; + +- (WebHistoryItem *)_globalHistoryItem; + +/*! + @method textIteratorForRect: + @param rect The rectangle of the document that we're interested in text from. + @result WebTextIterator object, initialized with a range that corresponds to + the passed-in rectangle. + @abstract This method gives the text for the approximate range of the document + corresponding to the rectangle. The range is determined by using hit testing at + the top left and bottom right of the rectangle. Because of that, there can be + text visible in the rectangle that is not included in the iterator. If you need + a guarantee of iterating all text that is visible, then you need to instead make + a WebTextIterator with a DOMRange that covers the entire document. + */ +- (WebTextIterator *)textIteratorForRect:(NSRect)rect; + +#if ENABLE_DASHBOARD_SUPPORT +// <rdar://problem/5217124> Clients other than Dashboard, don't use this. +// As of this writing, Dashboard uses this on Tiger, but not on Leopard or newer. +- (void)handleAuthenticationForResource:(id)identifier challenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)dataSource; +#endif + +- (void)_clearUndoRedoOperations; + +/* Used to do fast (lower quality) scaling of images so that window resize can be quick. */ +- (BOOL)_inFastImageScalingMode; +- (void)_setUseFastImageScalingMode:(BOOL)flag; + +- (BOOL)_cookieEnabled; +- (void)_setCookieEnabled:(BOOL)enable; + +// SPI for DumpRenderTree +- (void)_executeCoreCommandByName:(NSString *)name value:(NSString *)value; +- (void)_clearMainFrameName; + +- (void)_setCustomHTMLTokenizerTimeDelay:(double)timeDelay; +- (void)_setCustomHTMLTokenizerChunkSize:(int)chunkSize; + +- (id)_initWithFrame:(NSRect)f frameName:(NSString *)frameName groupName:(NSString *)groupName usesDocumentViews:(BOOL)usesDocumentViews; +- (BOOL)_usesDocumentViews; + +- (void)setSelectTrailingWhitespaceEnabled:(BOOL)flag; +- (BOOL)isSelectTrailingWhitespaceEnabled; + +- (void)setMemoryCacheDelegateCallsEnabled:(BOOL)suspend; +- (BOOL)areMemoryCacheDelegateCallsEnabled; + +- (void)_setJavaScriptURLsAreAllowed:(BOOL)setJavaScriptURLsAreAllowed; + ++ (NSCursor *)_pointingHandCursor; + +// SPI for DumpRenderTree +- (BOOL)_postsAcceleratedCompositingNotifications; +- (void)_setPostsAcceleratedCompositingNotifications:(BOOL)flag; +- (BOOL)_isUsingAcceleratedCompositing; + +// Returns YES if NSView -displayRectIgnoringOpacity:inContext: will produce a faithful representation of the content. +- (BOOL)_isSoftwareRenderable; +// When drawing into a bitmap context, we normally flatten compositing layers (and distort 3D transforms). +// Clients who are able to capture their own copy of the compositing layers need to be able to disable this. +- (void)_setIncludesFlattenedCompositingLayersWhenDrawingToBitmap:(BOOL)flag; +- (BOOL)_includesFlattenedCompositingLayersWhenDrawingToBitmap; + +// SPI for PluginHalter ++ (BOOL)_isNodeHaltedPlugin:(DOMNode *)node; ++ (BOOL)_hasPluginForNodeBeenHalted:(DOMNode *)node; ++ (void)_restartHaltedPluginForNode:(DOMNode *)node; + +// Which pasteboard text is coming from in editing delegate methods such as shouldInsertNode. +- (NSPasteboard *)_insertionPasteboard; + +// Whitelists access from an origin (sourceOrigin) to a set of one or more origins described by the parameters: +// - destinationProtocol: The protocol to grant access to. +// - destinationHost: The host to grant access to. +// - allowDestinationSubdomains: If host is a domain, setting this to YES will whitelist host and all its subdomains, recursively. ++ (void)_addOriginAccessWhitelistEntryWithSourceOrigin:(NSString *)sourceOrigin destinationProtocol:(NSString *)destinationProtocol destinationHost:(NSString *)destinationHost allowDestinationSubdomains:(BOOL)allowDestinationSubdomains; ++ (void)_removeOriginAccessWhitelistEntryWithSourceOrigin:(NSString *)sourceOrigin destinationProtocol:(NSString *)destinationProtocol destinationHost:(NSString *)destinationHost allowDestinationSubdomains:(BOOL)allowDestinationSubdomains; + +// Removes all white list entries created with _addOriginAccessWhitelistEntryWithSourceOrigin. ++ (void)_resetOriginAccessWhitelists; + +// FIXME: The following two methods are deprecated in favor of the overloads below that take the WebUserContentInjectedFrames argument. https://bugs.webkit.org/show_bug.cgi?id=41800. ++ (void)_addUserScriptToGroup:(NSString *)groupName world:(WebScriptWorld *)world source:(NSString *)source url:(NSURL *)url whitelist:(NSArray *)whitelist blacklist:(NSArray *)blacklist injectionTime:(WebUserScriptInjectionTime)injectionTime; ++ (void)_addUserStyleSheetToGroup:(NSString *)groupName world:(WebScriptWorld *)world source:(NSString *)source url:(NSURL *)url whitelist:(NSArray *)whitelist blacklist:(NSArray *)blacklist; + ++ (void)_addUserScriptToGroup:(NSString *)groupName world:(WebScriptWorld *)world source:(NSString *)source url:(NSURL *)url whitelist:(NSArray *)whitelist blacklist:(NSArray *)blacklist injectionTime:(WebUserScriptInjectionTime)injectionTime injectedFrames:(WebUserContentInjectedFrames)injectedFrames; ++ (void)_addUserStyleSheetToGroup:(NSString *)groupName world:(WebScriptWorld *)world source:(NSString *)source url:(NSURL *)url whitelist:(NSArray *)whitelist blacklist:(NSArray *)blacklist injectedFrames:(WebUserContentInjectedFrames)injectedFrames; ++ (void)_removeUserScriptFromGroup:(NSString *)groupName world:(WebScriptWorld *)world url:(NSURL *)url; ++ (void)_removeUserStyleSheetFromGroup:(NSString *)groupName world:(WebScriptWorld *)world url:(NSURL *)url; ++ (void)_removeUserScriptsFromGroup:(NSString *)groupName world:(WebScriptWorld *)world; ++ (void)_removeUserStyleSheetsFromGroup:(NSString *)groupName world:(WebScriptWorld *)world; ++ (void)_removeAllUserContentFromGroup:(NSString *)groupName; + +// SPI for DumpRenderTree ++ (void)_setLoadResourcesSerially:(BOOL)serialize; + +/*! + @method cssAnimationsSuspended + @abstract Returns whether or not CSS Animations are suspended. + @result YES if CSS Animations are suspended. +*/ +- (BOOL)cssAnimationsSuspended; + +/*! + @method setCSSAnimationsSuspended + @param paused YES to suspend animations, NO to resume animations. + @discussion Suspends or resumes all running animations and transitions in the page. +*/ +- (void)setCSSAnimationsSuspended:(BOOL)suspended; + ++ (void)_setDomainRelaxationForbidden:(BOOL)forbidden forURLScheme:(NSString *)scheme; ++ (void)_registerURLSchemeAsSecure:(NSString *)scheme; + +- (void)_scaleWebView:(float)scale atOrigin:(NSPoint)origin; +- (float)_viewScaleFactor; + +- (void)_setUseFixedLayout:(BOOL)fixed; +- (void)_setFixedLayoutSize:(NSSize)size; + +- (BOOL)_useFixedLayout; +- (NSSize)_fixedLayoutSize; + +// Deprecated. Use the methods in pending public above instead. +- (WebNSUInteger)markAllMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag highlight:(BOOL)highlight limit:(WebNSUInteger)limit; +- (WebNSUInteger)countMatchesForText:(NSString *)string caseSensitive:(BOOL)caseFlag highlight:(BOOL)highlight limit:(WebNSUInteger)limit markMatches:(BOOL)markMatches; + +/*! + @method searchFor:direction:caseSensitive:wrap:startInSelection: + @abstract Searches a document view for a string and highlights the string if it is found. + Starts the search from the current selection. Will search across all frames. + @param string The string to search for. + @param forward YES to search forward, NO to seach backwards. + @param caseFlag YES to for case-sensitive search, NO for case-insensitive search. + @param wrapFlag YES to wrap around, NO to avoid wrapping. + @param startInSelection YES to begin search in the selected text (useful for incremental searching), NO to begin search after the selected text. + @result YES if found, NO if not found. + */ +// Deprecated. Use findString. +- (BOOL)searchFor:(NSString *)string direction:(BOOL)forward caseSensitive:(BOOL)caseFlag wrap:(BOOL)wrapFlag startInSelection:(BOOL)startInSelection; + +@end + +@interface WebView (WebViewPrintingPrivate) +/*! + @method _adjustPrintingMarginsForHeaderAndFooter: + @abstract Increase the top and bottom margins for the current print operation to + account for the header and footer height. + @discussion Called by <WebDocument> implementors once when a print job begins. If the + <WebDocument> implementor implements knowsPageRange:, this should be called from there. + Otherwise this should be called from beginDocument. The <WebDocument> implementors need + to also call _drawHeaderAndFooter. +*/ +- (void)_adjustPrintingMarginsForHeaderAndFooter; + +/*! + @method _drawHeaderAndFooter + @abstract Gives the WebView's UIDelegate a chance to draw a header and footer on the + printed page. + @discussion This should be called by <WebDocument> implementors from an override of + drawPageBorderWithSize:. +*/ +- (void)_drawHeaderAndFooter; +@end + +@interface WebView (WebViewGrammarChecking) + +// FIXME: These two methods should be merged into WebViewEditing when we're not in API freeze +- (BOOL)isGrammarCheckingEnabled; +#ifndef BUILDING_ON_TIGER +- (void)setGrammarCheckingEnabled:(BOOL)flag; + +// FIXME: This method should be merged into WebIBActions when we're not in API freeze +- (void)toggleGrammarChecking:(id)sender; +#endif + +@end + +@interface WebView (WebViewTextChecking) + +- (BOOL)isAutomaticQuoteSubstitutionEnabled; +- (BOOL)isAutomaticLinkDetectionEnabled; +- (BOOL)isAutomaticDashSubstitutionEnabled; +- (BOOL)isAutomaticTextReplacementEnabled; +- (BOOL)isAutomaticSpellingCorrectionEnabled; +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) +- (void)setAutomaticQuoteSubstitutionEnabled:(BOOL)flag; +- (void)toggleAutomaticQuoteSubstitution:(id)sender; +- (void)setAutomaticLinkDetectionEnabled:(BOOL)flag; +- (void)toggleAutomaticLinkDetection:(id)sender; +- (void)setAutomaticDashSubstitutionEnabled:(BOOL)flag; +- (void)toggleAutomaticDashSubstitution:(id)sender; +- (void)setAutomaticTextReplacementEnabled:(BOOL)flag; +- (void)toggleAutomaticTextReplacement:(id)sender; +- (void)setAutomaticSpellingCorrectionEnabled:(BOOL)flag; +- (void)toggleAutomaticSpellingCorrection:(id)sender; +#endif + +@end + +@interface WebView (WebViewEditingInMail) +- (void)_insertNewlineInQuotedContent; +- (void)_replaceSelectionWithNode:(DOMNode *)node matchStyle:(BOOL)matchStyle; +- (BOOL)_selectionIsCaret; +- (BOOL)_selectionIsAll; +@end + +@interface WebView (WebViewDeviceOrientation) +- (void)_setDeviceOrientationProvider:(id<WebDeviceOrientationProvider>)deviceOrientationProvider; +- (id<WebDeviceOrientationProvider>)_deviceOrientationProvider; +@end + +@protocol WebGeolocationProvider <NSObject> +- (void)registerWebView:(WebView *)webView; +- (void)unregisterWebView:(WebView *)webView; +- (WebGeolocationPosition *)lastPosition; +@end + +@interface WebView (WebViewGeolocation) +- (void)_setGeolocationProvider:(id<WebGeolocationProvider>)locationProvider; +- (id<WebGeolocationProvider>)_geolocationProvider; + +- (void)_geolocationDidChangePosition:(WebGeolocationPosition *)position; +- (void)_geolocationDidFailWithError:(NSError *)error; +@end + +@interface WebView (WebViewPrivateStyleInfo) +- (JSValueRef)_computedStyleIncludingVisitedInfo:(JSContextRef)context forElement:(JSValueRef)value; +@end + +@interface WebView (WebViewPrivateNodesFromRect) +- (JSValueRef)_nodesFromRect:(JSContextRef)context forDocument:(JSValueRef)value x:(int)x y:(int)y top:(unsigned)top right:(unsigned)right bottom:(unsigned)bottom left:(unsigned)left ignoreClipping:(BOOL)ignoreClipping; +@end + +@interface NSObject (WebFrameLoadDelegatePrivate) +- (void)webView:(WebView *)sender didFirstLayoutInFrame:(WebFrame *)frame; + +// didFinishDocumentLoadForFrame is sent when the document has finished loading, though not necessarily all +// of its subresources. +// FIXME 5259339: Currently this callback is not sent for (some?) pages loaded entirely from the cache. +- (void)webView:(WebView *)sender didFinishDocumentLoadForFrame:(WebFrame *)frame; + +// Addresses 4192534. SPI for now. +- (void)webView:(WebView *)sender didHandleOnloadEventsForFrame:(WebFrame *)frame; + +- (void)webView:(WebView *)sender didFirstVisuallyNonEmptyLayoutInFrame:(WebFrame *)frame; + +// For implementing the WebInspector's test harness +- (void)webView:(WebView *)webView didClearInspectorWindowObject:(WebScriptObject *)windowObject forFrame:(WebFrame *)frame; + +@end + +@interface NSObject (WebResourceLoadDelegatePrivate) +// Addresses <rdar://problem/5008925> - SPI for now +- (NSCachedURLResponse *)webView:(WebView *)sender resource:(id)identifier willCacheResponse:(NSCachedURLResponse *)response fromDataSource:(WebDataSource *)dataSource; +@end + +#undef WebNSInteger +#undef WebNSUInteger diff --git a/Source/WebKit/mac/WebView/WebWindowAnimation.h b/Source/WebKit/mac/WebView/WebWindowAnimation.h new file mode 100644 index 0000000..ecf975b --- /dev/null +++ b/Source/WebKit/mac/WebView/WebWindowAnimation.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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 <Cocoa/Cocoa.h> + +@interface WebWindowScaleAnimation : NSAnimation { +@private + NSRect _initialFrame, _finalFrame, _realFrame; + NSWindow *_window; // (assign) + NSAnimation *_subAnimation; // (retain) + NSTimeInterval _hintedDuration; +} +- (id)initWithHintedDuration:(NSTimeInterval)duration window:(NSWindow *)window initalFrame:(NSRect)initialFrame finalFrame:(NSRect)finalFrame; + +- (void)setSubAnimation:(NSAnimation *)animation; + +- (NSRect)currentFrame; + +// Be sure to call setWindow:nil to clear the weak link _window when appropriate +- (void)setWindow:(NSWindow *)window; +@end + + +@interface WebWindowFadeAnimation : NSAnimation { +@private + CGFloat _initialAlpha, _finalAlpha; + NSWindow *_window; // (assign) + BOOL _isStopped; + +} +- (id)initWithDuration:(NSTimeInterval)duration window:(NSWindow *)window initialAlpha:(CGFloat)initialAlpha finalAlpha:(CGFloat)finalAlpha; + +- (CGFloat)currentAlpha; + +// Be sure to call setWindow:nil to clear the weak link _window when appropriate +- (void)setWindow:(NSWindow *)window; +@end diff --git a/Source/WebKit/mac/WebView/WebWindowAnimation.m b/Source/WebKit/mac/WebView/WebWindowAnimation.m new file mode 100644 index 0000000..6eadb21 --- /dev/null +++ b/Source/WebKit/mac/WebView/WebWindowAnimation.m @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR + * 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 "WebWindowAnimation.h" +#import "WebKitSystemInterface.h" +#import <wtf/Assertions.h> + +static const CGFloat slowMotionFactor = 10.; + +static NSTimeInterval WebWindowAnimationDurationFromDuration(NSTimeInterval duration) +{ + return ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) ? duration * slowMotionFactor : duration; +} + +static NSRect scaledRect(NSRect _initialFrame, NSRect _finalFrame, double factor) +{ + NSRect currentRect = _initialFrame; + currentRect.origin.x += (NSMinX(_finalFrame) - NSMinX(_initialFrame)) * factor; + currentRect.origin.y += (NSMinY(_finalFrame) - NSMinY(_initialFrame)) * factor; + currentRect.size.width += (NSWidth(_finalFrame) - NSWidth(_initialFrame)) * factor; + currentRect.size.height += (NSHeight(_finalFrame) - NSHeight(_initialFrame)) * factor; + return currentRect; +} + +static CGFloat squaredDistance(NSPoint point1, NSPoint point2) +{ + CGFloat deltaX = point1.x - point2.x; + CGFloat deltaY = point1.y - point2.y; + return deltaX * deltaX + deltaY * deltaY; +} + +@implementation WebWindowScaleAnimation + +- (id)init +{ + self = [super init]; + if (!self) + return nil; +#ifndef BUILDING_ON_TIGER + [self setAnimationBlockingMode:NSAnimationNonblockingThreaded]; +#endif + [self setFrameRate:60.]; + return self; +} + +- (id)initWithHintedDuration:(NSTimeInterval)duration window:(NSWindow *)window initalFrame:(NSRect)initialFrame finalFrame:(NSRect)finalFrame +{ + self = [self init]; + if (!self) + return nil; + _hintedDuration = duration; + _window = window; + _initialFrame = initialFrame; + _finalFrame = finalFrame; + _realFrame = [window frame]; + return self; +} + +- (void) dealloc +{ + [_subAnimation release]; + [super dealloc]; +} + +- (void)setDuration:(NSTimeInterval)duration +{ + [super setDuration:WebWindowAnimationDurationFromDuration(duration)]; +} + +- (void)setWindow:(NSWindow *)window +{ + _window = window; +} + +- (float)currentValue +{ + return 0.5 - 0.5 * cos(M_PI * (1 - [self currentProgress])); +} + +- (NSRect)currentFrame +{ + return scaledRect(_finalFrame, _initialFrame, [self currentValue]); +} + +- (void)setCurrentProgress:(NSAnimationProgress)progress +{ + if (!_window) + return; + + [super setCurrentProgress:progress]; + + NSRect currentRect = [self currentFrame]; +#ifndef BUILDING_ON_TIGER + WKWindowSetScaledFrame(_window, currentRect, _realFrame); +#else + [_window setFrame:currentRect display:YES]; +#endif + [_subAnimation setCurrentProgress:progress]; +} + +- (void)setSubAnimation:(NSAnimation *)animation +{ + id oldAnimation = _subAnimation; + _subAnimation = [animation retain]; + [oldAnimation release]; +} + +- (NSTimeInterval)additionalDurationNeededToReachFinalFrame +{ + static const CGFloat maxAdditionalDuration = 1.0; + static const CGFloat speedFactor = 0.0001; + + CGFloat maxDist = squaredDistance(_initialFrame.origin, _finalFrame.origin); + CGFloat dist; + + dist = squaredDistance(NSMakePoint(NSMaxX(_initialFrame), NSMinY(_initialFrame)), NSMakePoint(NSMaxX(_finalFrame), NSMinY(_finalFrame))); + if (dist > maxDist) + maxDist = dist; + + dist = squaredDistance(NSMakePoint(NSMaxX(_initialFrame), NSMaxY(_initialFrame)), NSMakePoint(NSMaxX(_finalFrame), NSMaxY(_finalFrame))); + if (dist > maxDist) + maxDist = dist; + + dist = squaredDistance(NSMakePoint(NSMinX(_initialFrame), NSMinY(_initialFrame)), NSMakePoint(NSMinX(_finalFrame), NSMinY(_finalFrame))); + if (dist > maxDist) + maxDist = dist; + + return MIN(sqrt(maxDist) * speedFactor, maxAdditionalDuration); +} + +- (void)startAnimation +{ + // Compute extra time + if (_hintedDuration) + [self setDuration:_hintedDuration + [self additionalDurationNeededToReachFinalFrame]]; + [super startAnimation]; +} + +- (void)stopAnimation +{ + _window = nil; + [super stopAnimation]; + [_subAnimation stopAnimation]; +} + +@end + +@implementation WebWindowFadeAnimation + +- (id)init +{ + self = [super init]; + if (!self) + return nil; +#ifndef BUILDING_ON_TIGER + [self setAnimationBlockingMode:NSAnimationNonblockingThreaded]; +#endif + [self setFrameRate:60]; + [self setAnimationCurve:NSAnimationEaseInOut]; + return self; +} + +- (id)initWithDuration:(NSTimeInterval)duration window:(NSWindow *)window initialAlpha:(CGFloat)initialAlpha finalAlpha:(CGFloat)finalAlpha +{ + self = [self init]; + if (!self) + return nil; + _window = window; + _initialAlpha = initialAlpha; + _finalAlpha = finalAlpha; + return self; +} + +- (void)setDuration:(NSTimeInterval)duration +{ + [super setDuration:WebWindowAnimationDurationFromDuration(duration)]; +} + +- (CGFloat)currentAlpha +{ + return MAX(0.0, MIN(1.0, _initialAlpha + [self currentValue] * (_finalAlpha - _initialAlpha))); +} + +- (void)setCurrentProgress:(NSAnimationProgress)progress +{ + if (_isStopped) + return; + + ASSERT(_window); + [super setCurrentProgress:progress]; + +#ifndef BUILDING_ON_TIGER + WKWindowSetAlpha(_window, [self currentAlpha]); +#else + [_window setAlphaValue:[self currentAlpha]]; +#endif +} + +- (void)setWindow:(NSWindow*)window +{ + _window = window; +} + +- (void)stopAnimation +{ + // This is relevant when we are a sub animation of a scale animation. + // In this case we are hosted in the animated thread of the parent + // and even after [super stopAnimation], the parent might call + // setCurrrentProgress. + _isStopped = YES; + + [super stopAnimation]; +} + +@end + |