diff options
Diffstat (limited to 'Source/WebKit/mac/Plugins')
64 files changed, 16296 insertions, 0 deletions
diff --git a/Source/WebKit/mac/Plugins/Hosted/HostedNetscapePluginStream.h b/Source/WebKit/mac/Plugins/Hosted/HostedNetscapePluginStream.h new file mode 100644 index 0000000..34cea32 --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/HostedNetscapePluginStream.h @@ -0,0 +1,106 @@ +/* + * 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. + * + * 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. + */ + +#if USE(PLUGIN_HOST_PROCESS) + +#ifndef HostedNetscapePluginStream_h +#define HostedNetscapePluginStream_h + +#include <WebCore/NetscapePlugInStreamLoader.h> +#include <WebKit/npapi.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/RetainPtr.h> + +namespace WebCore { + class FrameLoader; + class NetscapePlugInStreamLoader; +} + +namespace WebKit { + +class NetscapePluginInstanceProxy; + +class HostedNetscapePluginStream : public RefCounted<HostedNetscapePluginStream> + , private WebCore::NetscapePlugInStreamLoaderClient { +public: + static PassRefPtr<HostedNetscapePluginStream> create(NetscapePluginInstanceProxy* instance, uint32_t streamID, NSURLRequest *request) + { + return adoptRef(new HostedNetscapePluginStream(instance, streamID, request)); + } + static PassRefPtr<HostedNetscapePluginStream> create(NetscapePluginInstanceProxy* instance, WebCore::FrameLoader* frameLoader) + { + return adoptRef(new HostedNetscapePluginStream(instance, frameLoader)); + } + + ~HostedNetscapePluginStream(); + + uint32_t streamID() const { return m_streamID; } + + void startStreamWithResponse(NSURLResponse *response); + void didReceiveData(WebCore::NetscapePlugInStreamLoader*, const char* bytes, int length); + void didFinishLoading(WebCore::NetscapePlugInStreamLoader*); + void didFail(WebCore::NetscapePlugInStreamLoader*, const WebCore::ResourceError&); + + void start(); + void stop(); + + void cancelLoad(NPReason reason); + + static NPReason reasonForError(NSError* error); + +private: + NSError *errorForReason(NPReason) const; + void cancelLoad(NSError *); + + HostedNetscapePluginStream(NetscapePluginInstanceProxy*, uint32_t streamID, NSURLRequest *); + HostedNetscapePluginStream(NetscapePluginInstanceProxy*, WebCore::FrameLoader*); + + void startStream(NSURL *, long long expectedContentLength, NSDate *lastModifiedDate, NSString *mimeType, NSData *headers); + + NSError *pluginCancelledConnectionError() const; + + // NetscapePlugInStreamLoaderClient methods. + void didReceiveResponse(WebCore::NetscapePlugInStreamLoader*, const WebCore::ResourceResponse&); + bool wantsAllStreams() const; + + RefPtr<NetscapePluginInstanceProxy> m_instance; + uint32_t m_streamID; + bool m_isTerminated; + RetainPtr<NSMutableURLRequest> m_request; + + RetainPtr<NSURL> m_requestURL; + RetainPtr<NSURL> m_responseURL; + RetainPtr<NSString> m_mimeType; + + WebCore::FrameLoader* m_frameLoader; + RefPtr<WebCore::NetscapePlugInStreamLoader> m_loader; +}; + +} + +#endif // HostedNetscapePluginStream_h +#endif // USE(PLUGIN_HOST_PROCESS) diff --git a/Source/WebKit/mac/Plugins/Hosted/HostedNetscapePluginStream.mm b/Source/WebKit/mac/Plugins/Hosted/HostedNetscapePluginStream.mm new file mode 100644 index 0000000..8ac1389 --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/HostedNetscapePluginStream.mm @@ -0,0 +1,276 @@ +/* + * 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. + * + * 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. + */ + +#if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API) + +#import "HostedNetscapePluginStream.h" + +#import "NetscapePluginHostProxy.h" +#import "NetscapePluginInstanceProxy.h" +#import "WebFrameInternal.h" +#import "WebHostedNetscapePluginView.h" +#import "WebKitErrorsPrivate.h" +#import "WebKitPluginHost.h" +#import "WebKitSystemInterface.h" +#import "WebNSURLExtras.h" +#import "WebNSURLRequestExtras.h" +#import <WebCore/DocumentLoader.h> +#import <WebCore/Frame.h> +#import <WebCore/FrameLoader.h> +#import <WebCore/ResourceLoadScheduler.h> +#import <WebCore/SecurityOrigin.h> +#import <WebCore/WebCoreURLResponse.h> +#import <wtf/RefCountedLeakCounter.h> + +using namespace WebCore; + +namespace WebKit { + +#ifndef NDEBUG +static WTF::RefCountedLeakCounter hostedNetscapePluginStreamCounter("HostedNetscapePluginStream"); +#endif + +HostedNetscapePluginStream::HostedNetscapePluginStream(NetscapePluginInstanceProxy* instance, uint32_t streamID, NSURLRequest *request) + : m_instance(instance) + , m_streamID(streamID) + , m_isTerminated(false) + , m_request(AdoptNS, [request mutableCopy]) + , m_requestURL([request URL]) + , m_frameLoader(0) +{ + if (SecurityOrigin::shouldHideReferrer([request URL], core([instance->pluginView() webFrame])->loader()->outgoingReferrer())) + [m_request.get() _web_setHTTPReferrer:nil]; + +#ifndef NDEBUG + hostedNetscapePluginStreamCounter.increment(); +#endif +} + +HostedNetscapePluginStream::HostedNetscapePluginStream(NetscapePluginInstanceProxy* instance, WebCore::FrameLoader* frameLoader) + : m_instance(instance) + , m_streamID(1) + , m_isTerminated(false) + , m_frameLoader(frameLoader) +{ +#ifndef NDEBUG + hostedNetscapePluginStreamCounter.increment(); +#endif +} + +HostedNetscapePluginStream::~HostedNetscapePluginStream() +{ +#ifndef NDEBUG + hostedNetscapePluginStreamCounter.decrement(); +#endif +} + +void HostedNetscapePluginStream::startStreamWithResponse(NSURLResponse *response) +{ + didReceiveResponse(0, response); +} + +void HostedNetscapePluginStream::startStream(NSURL *responseURL, long long expectedContentLength, NSDate *lastModifiedDate, NSString *mimeType, NSData *headers) +{ + m_responseURL = responseURL; + m_mimeType = mimeType; + + char* mimeTypeUTF8 = const_cast<char*>([mimeType UTF8String]); + int mimeTypeUTF8Length = mimeTypeUTF8 ? strlen (mimeTypeUTF8) + 1 : 0; + + const char *url = [responseURL _web_URLCString]; + int urlLength = url ? strlen(url) + 1 : 0; + + _WKPHStartStream(m_instance->hostProxy()->port(), + m_instance->pluginID(), + m_streamID, + const_cast<char*>(url), urlLength, + expectedContentLength, + [lastModifiedDate timeIntervalSince1970], + mimeTypeUTF8, mimeTypeUTF8Length, + const_cast<char*>(reinterpret_cast<const char*>([headers bytes])), [headers length]); +} + +void HostedNetscapePluginStream::didReceiveData(WebCore::NetscapePlugInStreamLoader*, const char* bytes, int length) +{ + _WKPHStreamDidReceiveData(m_instance->hostProxy()->port(), + m_instance->pluginID(), + m_streamID, + const_cast<char*>(bytes), length); +} + +void HostedNetscapePluginStream::didFinishLoading(WebCore::NetscapePlugInStreamLoader*) +{ + _WKPHStreamDidFinishLoading(m_instance->hostProxy()->port(), + m_instance->pluginID(), + m_streamID); + m_instance->disconnectStream(this); +} + +void HostedNetscapePluginStream::didReceiveResponse(NetscapePlugInStreamLoader*, const ResourceResponse& response) +{ + NSURLResponse *r = response.nsURLResponse(); + + NSMutableData *theHeaders = nil; + long long expectedContentLength = [r expectedContentLength]; + + if ([r isKindOfClass:[NSHTTPURLResponse class]]) { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)r; + theHeaders = [NSMutableData dataWithCapacity:1024]; + + // FIXME: it would be nice to be able to get the raw HTTP header block. + // This includes the HTTP version, the real status text, + // all headers in their original order and including duplicates, + // and all original bytes verbatim, rather than sent through Unicode translation. + // Unfortunately NSHTTPURLResponse doesn't provide access at that low a level. + + [theHeaders appendBytes:"HTTP " length:5]; + char statusStr[10]; + long statusCode = [httpResponse statusCode]; + snprintf(statusStr, sizeof(statusStr), "%ld", statusCode); + [theHeaders appendBytes:statusStr length:strlen(statusStr)]; + [theHeaders appendBytes:" OK\n" length:4]; + + // HACK: pass the headers through as UTF-8. + // This is not the intended behavior; we're supposed to pass original bytes verbatim. + // But we don't have the original bytes, we have NSStrings built by the URL loading system. + // It hopefully shouldn't matter, since RFC2616/RFC822 require ASCII-only headers, + // but surely someone out there is using non-ASCII characters, and hopefully UTF-8 is adequate here. + // It seems better than NSASCIIStringEncoding, which will lose information if non-ASCII is used. + + NSDictionary *headerDict = [httpResponse allHeaderFields]; + NSArray *keys = [[headerDict allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; + NSEnumerator *i = [keys objectEnumerator]; + NSString *k; + while ((k = [i nextObject]) != nil) { + NSString *v = [headerDict objectForKey:k]; + [theHeaders appendData:[k dataUsingEncoding:NSUTF8StringEncoding]]; + [theHeaders appendBytes:": " length:2]; + [theHeaders appendData:[v dataUsingEncoding:NSUTF8StringEncoding]]; + [theHeaders appendBytes:"\n" length:1]; + } + + // If the content is encoded (most likely compressed), then don't send its length to the plugin, + // which is only interested in the decoded length, not yet known at the moment. + // <rdar://problem/4470599> tracks a request for -[NSURLResponse expectedContentLength] to incorporate this logic. + NSString *contentEncoding = (NSString *)[[(NSHTTPURLResponse *)r allHeaderFields] objectForKey:@"Content-Encoding"]; + if (contentEncoding && ![contentEncoding isEqualToString:@"identity"]) + expectedContentLength = -1; + + [theHeaders appendBytes:"\0" length:1]; + } + + startStream([r URL], expectedContentLength, WKGetNSURLResponseLastModifiedDate(r), [r MIMEType], theHeaders); +} + +NPReason HostedNetscapePluginStream::reasonForError(NSError *error) +{ + if (!error) + return NPRES_DONE; + + if ([[error domain] isEqualToString:NSURLErrorDomain] && [error code] == NSURLErrorCancelled) + return NPRES_USER_BREAK; + + return NPRES_NETWORK_ERR; +} + +void HostedNetscapePluginStream::didFail(WebCore::NetscapePlugInStreamLoader*, const WebCore::ResourceError& error) +{ + if (NetscapePluginHostProxy* hostProxy = m_instance->hostProxy()) + _WKPHStreamDidFail(hostProxy->port(), m_instance->pluginID(), m_streamID, reasonForError(error)); + m_instance->disconnectStream(this); +} + +bool HostedNetscapePluginStream::wantsAllStreams() const +{ + // FIXME: Implement. + return false; +} + +void HostedNetscapePluginStream::start() +{ + ASSERT(m_request); + ASSERT(!m_frameLoader); + ASSERT(!m_loader); + + m_loader = resourceLoadScheduler()->schedulePluginStreamLoad(core([m_instance->pluginView() webFrame]), this, m_request.get()); +} + +void HostedNetscapePluginStream::stop() +{ + ASSERT(!m_frameLoader); + + if (!m_loader->isDone()) + m_loader->cancel(m_loader->cancelledError()); +} + +void HostedNetscapePluginStream::cancelLoad(NPReason reason) +{ + cancelLoad(errorForReason(reason)); +} + +void HostedNetscapePluginStream::cancelLoad(NSError *error) +{ + if (m_frameLoader) { + ASSERT(!m_loader); + + DocumentLoader* documentLoader = m_frameLoader->activeDocumentLoader(); + if (documentLoader && documentLoader->isLoadingMainResource()) + documentLoader->cancelMainResourceLoad(error); + return; + } + + if (!m_loader->isDone()) { + // Cancelling the load will disconnect the stream so there's no need to do it explicitly. + m_loader->cancel(error); + } else + m_instance->disconnectStream(this); +} + +NSError *HostedNetscapePluginStream::pluginCancelledConnectionError() const +{ + return [[[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInCancelledConnection + contentURL:m_responseURL ? m_responseURL.get() : m_requestURL.get() + pluginPageURL:nil + pluginName:[[m_instance->pluginView() pluginPackage] pluginInfo].name + MIMEType:m_mimeType.get()] autorelease]; +} + +NSError *HostedNetscapePluginStream::errorForReason(NPReason reason) const +{ + if (reason == NPRES_DONE) + return nil; + + if (reason == NPRES_USER_BREAK) + return [NSError _webKitErrorWithDomain:NSURLErrorDomain + code:NSURLErrorCancelled + URL:m_responseURL ? m_responseURL.get() : m_requestURL.get()]; + + return pluginCancelledConnectionError(); +} + +} // namespace WebKit + +#endif // USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API) + diff --git a/Source/WebKit/mac/Plugins/Hosted/NetscapePluginHostManager.h b/Source/WebKit/mac/Plugins/Hosted/NetscapePluginHostManager.h new file mode 100644 index 0000000..4e59456 --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/NetscapePluginHostManager.h @@ -0,0 +1,75 @@ +/* + * 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. + * + * 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. + */ + +#if USE(PLUGIN_HOST_PROCESS) + +#ifndef NetscapePluginHostManager_h +#define NetscapePluginHostManager_h + +#import <WebCore/PlatformString.h> +#import <wtf/HashMap.h> +#import <wtf/PassRefPtr.h> +#import <wtf/text/StringHash.h> + +@class WebHostedNetscapePluginView; + +namespace WebKit { + +class NetscapePluginInstanceProxy; +class NetscapePluginHostProxy; + +class NetscapePluginHostManager { +public: + static NetscapePluginHostManager& shared(); + + PassRefPtr<NetscapePluginInstanceProxy> instantiatePlugin(const WTF::String& pluginPath, cpu_type_t pluginArchitecture, const WTF::String& bundleIdentifier, WebHostedNetscapePluginView *, NSString *mimeType, NSArray *attributeKeys, NSArray *attributeValues, NSString *userAgent, NSURL *sourceURL, bool fullFrame, bool isPrivateBrowsingEnabled, bool isAcceleratedCompositingEnabled); + + void pluginHostDied(NetscapePluginHostProxy*); + + static void createPropertyListFile(const WTF::String& pluginPath, cpu_type_t pluginArchitecture); + + void didCreateWindow(); + +private: + NetscapePluginHostProxy* hostForPlugin(const WTF::String& pluginPath, cpu_type_t pluginArchitecture, const WTF::String& bundleIdentifier); + + NetscapePluginHostManager(); + ~NetscapePluginHostManager(); + + bool spawnPluginHost(const WTF::String& pluginPath, cpu_type_t pluginArchitecture, mach_port_t clientPort, mach_port_t& pluginHostPort, ProcessSerialNumber& pluginHostPSN); + + bool initializeVendorPort(); + + mach_port_t m_pluginVendorPort; + + // FIXME: This should really be a HashMap of RetainPtrs, but that doesn't work right now. + typedef HashMap<WTF::String, NetscapePluginHostProxy*> PluginHostMap; + PluginHostMap m_pluginHosts; +}; + +} // namespace WebKit + +#endif // NetscapePluginHostManager_h +#endif // USE(PLUGIN_HOST_PROCESS) diff --git a/Source/WebKit/mac/Plugins/Hosted/NetscapePluginHostManager.mm b/Source/WebKit/mac/Plugins/Hosted/NetscapePluginHostManager.mm new file mode 100644 index 0000000..ce40f48 --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/NetscapePluginHostManager.mm @@ -0,0 +1,321 @@ +/* + * 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. + * + * 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. + */ + +#if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API) + +#import "NetscapePluginHostManager.h" + +#import "NetscapePluginHostProxy.h" +#import "NetscapePluginInstanceProxy.h" +#import "WebLocalizableStringsInternal.h" +#import "WebKitSystemInterface.h" +#import "WebNetscapePluginPackage.h" +#import <mach/mach_port.h> +#import <servers/bootstrap.h> +#import <spawn.h> +#import <wtf/Assertions.h> +#import <wtf/RetainPtr.h> +#import <wtf/StdLibExtras.h> + +extern "C" { +#import "WebKitPluginAgent.h" +#import "WebKitPluginHost.h" +} + +using namespace std; +using namespace WebCore; + +namespace WebKit { + +NetscapePluginHostManager& NetscapePluginHostManager::shared() +{ + DEFINE_STATIC_LOCAL(NetscapePluginHostManager, pluginHostManager, ()); + + return pluginHostManager; +} + +static NSString * const pluginHostAppName = @"WebKitPluginHost.app"; + +NetscapePluginHostManager::NetscapePluginHostManager() + : m_pluginVendorPort(MACH_PORT_NULL) +{ +} + +NetscapePluginHostManager::~NetscapePluginHostManager() +{ +} + +NetscapePluginHostProxy* NetscapePluginHostManager::hostForPlugin(const WTF::String& pluginPath, cpu_type_t pluginArchitecture, const String& bundleIdentifier) +{ + pair<PluginHostMap::iterator, bool> result = m_pluginHosts.add(pluginPath, 0); + + // The package was already in the map, just return it. + if (!result.second) + return result.first->second; + + mach_port_t clientPort; + if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &clientPort) != KERN_SUCCESS) { + m_pluginHosts.remove(result.first); + return 0; + } + + mach_port_t pluginHostPort; + ProcessSerialNumber pluginHostPSN; + if (!spawnPluginHost(pluginPath, pluginArchitecture, clientPort, pluginHostPort, pluginHostPSN)) { + mach_port_destroy(mach_task_self(), clientPort); + m_pluginHosts.remove(result.first); + return 0; + } + + // Since Flash NPObjects add methods dynamically, we don't want to cache when a property/method doesn't exist + // on an object because it could be added later. + bool shouldCacheMissingPropertiesAndMethods = bundleIdentifier != "com.macromedia.Flash Player.plugin"; + + NetscapePluginHostProxy* hostProxy = new NetscapePluginHostProxy(clientPort, pluginHostPort, pluginHostPSN, shouldCacheMissingPropertiesAndMethods); + + result.first->second = hostProxy; + + return hostProxy; +} + +bool NetscapePluginHostManager::spawnPluginHost(const String& pluginPath, cpu_type_t pluginArchitecture, mach_port_t clientPort, mach_port_t& pluginHostPort, ProcessSerialNumber& pluginHostPSN) +{ + if (m_pluginVendorPort == MACH_PORT_NULL) { + if (!initializeVendorPort()) + return false; + } + + mach_port_t renderServerPort = WKInitializeRenderServer(); + if (renderServerPort == MACH_PORT_NULL) + return false; + + NSString *pluginHostAppPath = [[NSBundle bundleWithIdentifier:@"com.apple.WebKit"] pathForAuxiliaryExecutable:pluginHostAppName]; + NSString *pluginHostAppExecutablePath = [[NSBundle bundleWithPath:pluginHostAppPath] executablePath]; + + RetainPtr<CFStringRef> localization(AdoptCF, WKCopyCFLocalizationPreferredName(NULL)); + + NSDictionary *launchProperties = [[NSDictionary alloc] initWithObjectsAndKeys: + pluginHostAppExecutablePath, @"pluginHostPath", + [NSNumber numberWithInt:pluginArchitecture], @"cpuType", + localization.get(), @"localization", + nil]; + + NSData *data = [NSPropertyListSerialization dataFromPropertyList:launchProperties format:NSPropertyListBinaryFormat_v1_0 errorDescription:0]; + ASSERT(data); + + [launchProperties release]; + + kern_return_t kr = _WKPASpawnPluginHost(m_pluginVendorPort, reinterpret_cast<uint8_t*>(const_cast<void*>([data bytes])), [data length], &pluginHostPort); + + if (kr == MACH_SEND_INVALID_DEST) { + // The plug-in vendor port has gone away for some reason. Try to reinitialize it. + m_pluginVendorPort = MACH_PORT_NULL; + if (!initializeVendorPort()) + return false; + + // And spawn the plug-in host again. + kr = _WKPASpawnPluginHost(m_pluginVendorPort, reinterpret_cast<uint8_t*>(const_cast<void*>([data bytes])), [data length], &pluginHostPort); + } + + if (kr != KERN_SUCCESS) { + // FIXME: Check for invalid dest and try to re-spawn the plug-in agent. + LOG_ERROR("Failed to spawn plug-in host, error %x", kr); + return false; + } + + NSString *visibleName = [NSString stringWithFormat:UI_STRING_INTERNAL("%@ (%@ Internet plug-in)", + "visible name of the plug-in host process. The first argument is the plug-in name " + "and the second argument is the application name."), + [[(NSString*)pluginPath lastPathComponent] stringByDeletingPathExtension], [[NSProcessInfo processInfo] processName]]; + + NSDictionary *hostProperties = [[NSDictionary alloc] initWithObjectsAndKeys: + visibleName, @"visibleName", + (NSString *)pluginPath, @"bundlePath", + nil]; + + data = [NSPropertyListSerialization dataFromPropertyList:hostProperties format:NSPropertyListBinaryFormat_v1_0 errorDescription:nil]; + ASSERT(data); + + [hostProperties release]; + + ProcessSerialNumber psn; + GetCurrentProcess(&psn); + + kr = _WKPHCheckInWithPluginHost(pluginHostPort, (uint8_t*)[data bytes], [data length], clientPort, psn.highLongOfPSN, psn.lowLongOfPSN, renderServerPort, + &pluginHostPSN.highLongOfPSN, &pluginHostPSN.lowLongOfPSN); + + if (kr != KERN_SUCCESS) { + mach_port_deallocate(mach_task_self(), pluginHostPort); + LOG_ERROR("Failed to check in with plug-in host, error %x", kr); + + return false; + } + + return true; +} + +bool NetscapePluginHostManager::initializeVendorPort() +{ + ASSERT(m_pluginVendorPort == MACH_PORT_NULL); + + // Get the plug-in agent port. + mach_port_t pluginAgentPort; + if (bootstrap_look_up(bootstrap_port, "com.apple.WebKit.PluginAgent", &pluginAgentPort) != KERN_SUCCESS) { + LOG_ERROR("Failed to look up the plug-in agent port"); + return false; + } + + NSData *appNameData = [[[NSProcessInfo processInfo] processName] dataUsingEncoding:NSUTF8StringEncoding]; + + // Tell the plug-in agent that we exist. + if (_WKPACheckInApplication(pluginAgentPort, (uint8_t*)[appNameData bytes], [appNameData length], &m_pluginVendorPort) != KERN_SUCCESS) + return false; + + // FIXME: Should we add a notification for when the vendor port dies? + + return true; +} + +void NetscapePluginHostManager::pluginHostDied(NetscapePluginHostProxy* pluginHost) +{ + PluginHostMap::iterator end = m_pluginHosts.end(); + + // This has O(n) complexity but the number of active plug-in hosts is very small so it shouldn't matter. + for (PluginHostMap::iterator it = m_pluginHosts.begin(); it != end; ++it) { + if (it->second == pluginHost) { + m_pluginHosts.remove(it); + return; + } + } +} + +PassRefPtr<NetscapePluginInstanceProxy> NetscapePluginHostManager::instantiatePlugin(const String& pluginPath, cpu_type_t pluginArchitecture, const String& bundleIdentifier, WebHostedNetscapePluginView *pluginView, NSString *mimeType, NSArray *attributeKeys, NSArray *attributeValues, NSString *userAgent, NSURL *sourceURL, bool fullFrame, bool isPrivateBrowsingEnabled, bool isAcceleratedCompositingEnabled) +{ + NetscapePluginHostProxy* hostProxy = hostForPlugin(pluginPath, pluginArchitecture, bundleIdentifier); + if (!hostProxy) + return 0; + + RetainPtr<NSMutableDictionary> properties(AdoptNS, [[NSMutableDictionary alloc] init]); + + if (mimeType) + [properties.get() setObject:mimeType forKey:@"mimeType"]; + + ASSERT_ARG(userAgent, userAgent); + [properties.get() setObject:userAgent forKey:@"userAgent"]; + + ASSERT_ARG(attributeKeys, attributeKeys); + [properties.get() setObject:attributeKeys forKey:@"attributeKeys"]; + + ASSERT_ARG(attributeValues, attributeValues); + [properties.get() setObject:attributeValues forKey:@"attributeValues"]; + + if (sourceURL) + [properties.get() setObject:[sourceURL absoluteString] forKey:@"sourceURL"]; + + [properties.get() setObject:[NSNumber numberWithBool:fullFrame] forKey:@"fullFrame"]; + [properties.get() setObject:[NSNumber numberWithBool:isPrivateBrowsingEnabled] forKey:@"privateBrowsingEnabled"]; + [properties.get() setObject:[NSNumber numberWithBool:isAcceleratedCompositingEnabled] forKey:@"acceleratedCompositingEnabled"]; + + NSData *data = [NSPropertyListSerialization dataFromPropertyList:properties.get() format:NSPropertyListBinaryFormat_v1_0 errorDescription:nil]; + ASSERT(data); + + RefPtr<NetscapePluginInstanceProxy> instance = NetscapePluginInstanceProxy::create(hostProxy, pluginView, fullFrame); + uint32_t requestID = instance->nextRequestID(); + kern_return_t kr = _WKPHInstantiatePlugin(hostProxy->port(), requestID, (uint8_t*)[data bytes], [data length], instance->pluginID()); + if (kr == MACH_SEND_INVALID_DEST) { + // Invalidate the instance. + instance->invalidate(); + + // The plug-in host must have died, but we haven't received the death notification yet. + pluginHostDied(hostProxy); + + // Try to spawn it again. + hostProxy = hostForPlugin(pluginPath, pluginArchitecture, bundleIdentifier); + + // Create a new instance. + instance = NetscapePluginInstanceProxy::create(hostProxy, pluginView, fullFrame); + requestID = instance->nextRequestID(); + kr = _WKPHInstantiatePlugin(hostProxy->port(), requestID, (uint8_t*)[data bytes], [data length], instance->pluginID()); + } + + auto_ptr<NetscapePluginInstanceProxy::InstantiatePluginReply> reply = instance->waitForReply<NetscapePluginInstanceProxy::InstantiatePluginReply>(requestID); + if (!reply.get() || reply->m_resultCode != KERN_SUCCESS) { + instance->cleanup(); + return 0; + } + + instance->setRenderContextID(reply->m_renderContextID); + instance->setRendererType(reply->m_rendererType); + + return instance.release(); +} + +void NetscapePluginHostManager::createPropertyListFile(const String& pluginPath, cpu_type_t pluginArchitecture) +{ + NSString *pluginHostAppPath = [[NSBundle bundleWithIdentifier:@"com.apple.WebKit"] pathForAuxiliaryExecutable:pluginHostAppName]; + NSString *pluginHostAppExecutablePath = [[NSBundle bundleWithPath:pluginHostAppPath] executablePath]; + NSString *bundlePath = pluginPath; + + pid_t pid; + posix_spawnattr_t attr; + posix_spawnattr_init(&attr); + + // Set the architecture. + size_t ocount = 0; + int cpuTypes[] = { pluginArchitecture }; + posix_spawnattr_setbinpref_np(&attr, 1, cpuTypes, &ocount); + + // Spawn the plug-in host and tell it to call the registration function. + const char* args[] = { [pluginHostAppExecutablePath fileSystemRepresentation], "-createPluginMIMETypesPreferences", [bundlePath fileSystemRepresentation], 0 }; + + int result = posix_spawn(&pid, args[0], 0, &attr, const_cast<char* const*>(args), 0); + posix_spawnattr_destroy(&attr); + + if (!result && pid > 0) { + // Wait for the process to finish. + while (waitpid(pid, 0, 0) == -1) { } + } +} + +void NetscapePluginHostManager::didCreateWindow() +{ + // See if any of our hosts are in full-screen mode. + PluginHostMap::iterator end = m_pluginHosts.end(); + for (PluginHostMap::iterator it = m_pluginHosts.begin(); it != end; ++it) { + NetscapePluginHostProxy* hostProxy = it->second; + + if (!hostProxy->isMenuBarVisible()) { + // Make ourselves the front process. + ProcessSerialNumber psn; + GetCurrentProcess(&psn); + SetFrontProcess(&psn); + return; + } + } +} + +} // namespace WebKit + +#endif // USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API) diff --git a/Source/WebKit/mac/Plugins/Hosted/NetscapePluginHostProxy.h b/Source/WebKit/mac/Plugins/Hosted/NetscapePluginHostProxy.h new file mode 100644 index 0000000..20db5d2 --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/NetscapePluginHostProxy.h @@ -0,0 +1,106 @@ +/* + * 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. + * + * 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. + */ + +#if USE(PLUGIN_HOST_PROCESS) + +#ifndef NetscapePluginHostProxy_h +#define NetscapePluginHostProxy_h + +#include <dispatch/dispatch.h> +#include <wtf/HashMap.h> +#include <wtf/RetainPtr.h> +#include <wtf/PassRefPtr.h> + +@class WebPlaceholderModalWindow; + +namespace WebKit { + +class NetscapePluginInstanceProxy; + +class NetscapePluginHostProxy { +public: + NetscapePluginHostProxy(mach_port_t clientPort, mach_port_t pluginHostPort, const ProcessSerialNumber& pluginHostPSN, bool shouldCacheMissingPropertiesAndMethods); + + mach_port_t port() const { ASSERT(fastMallocSize(this)); return m_pluginHostPort; } + mach_port_t clientPort() const { ASSERT(fastMallocSize(this)); return m_clientPort; } + + void addPluginInstance(NetscapePluginInstanceProxy*); + void removePluginInstance(NetscapePluginInstanceProxy*); + + NetscapePluginInstanceProxy* pluginInstance(uint32_t pluginID); + + bool isMenuBarVisible() const { return m_menuBarIsVisible; } + void setMenuBarVisible(bool); + + bool isFullscreenWindowShowing() const { return m_fullscreenWindowIsShowing; } + void setFullscreenWindowIsShowing(bool); + + void setModal(bool); + + void applicationDidBecomeActive(); + + bool processRequests(); + static bool isProcessingRequests() { return s_processingRequests; } + + bool shouldCacheMissingPropertiesAndMethods() const { return m_shouldCacheMissingPropertiesAndMethods; } + +private: + ~NetscapePluginHostProxy(); + void pluginHostDied(); + + void beginModal(); + void endModal(); + + void didEnterFullscreen() const; + void didExitFullscreen() const; + + static void deadNameNotificationCallback(CFMachPortRef, void *msg, CFIndex size, void *info); + + typedef HashMap<uint32_t, RefPtr<NetscapePluginInstanceProxy> > PluginInstanceMap; + PluginInstanceMap m_instances; + + mach_port_t m_clientPort; + mach_port_t m_portSet; + + RetainPtr<CFRunLoopSourceRef> m_clientPortSource; + mach_port_t m_pluginHostPort; + RetainPtr<CFMachPortRef> m_deadNameNotificationPort; + + RetainPtr<id> m_activationObserver; + RetainPtr<WebPlaceholderModalWindow *> m_placeholderWindow; + unsigned m_isModal; + bool m_menuBarIsVisible; + bool m_fullscreenWindowIsShowing; + const ProcessSerialNumber m_pluginHostPSN; + + static unsigned s_processingRequests; + + bool m_shouldCacheMissingPropertiesAndMethods; +}; + +} // namespace WebKit + +#endif // NetscapePluginHostProxy_h +#endif // USE(PLUGIN_HOST_PROCESS) diff --git a/Source/WebKit/mac/Plugins/Hosted/NetscapePluginHostProxy.mm b/Source/WebKit/mac/Plugins/Hosted/NetscapePluginHostProxy.mm new file mode 100644 index 0000000..4506f03 --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/NetscapePluginHostProxy.mm @@ -0,0 +1,1134 @@ +/* + * 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. + * + * 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. + */ + +#if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API) + +#import "NetscapePluginHostProxy.h" + +#import <mach/mach.h> +#import <wtf/StdLibExtras.h> + +#import "HostedNetscapePluginStream.h" +#import "NetscapePluginHostManager.h" +#import "NetscapePluginInstanceProxy.h" +#import "WebFrameInternal.h" +#import "WebHostedNetscapePluginView.h" +#import "WebKitSystemInterface.h" +#import <WebCore/Frame.h> +#import <WebCore/IdentifierRep.h> +#import <WebCore/ScriptController.h> +#import <string> + +extern "C" { +#import "WebKitPluginHost.h" +#import "WebKitPluginClientServer.h" +} + +using namespace std; +using namespace JSC; +using namespace WebCore; + +@interface WebPlaceholderModalWindow : NSWindow +@end + +@implementation WebPlaceholderModalWindow +// Prevent NSApp from calling requestUserAttention: when the window is shown +// modally, even if the app is inactive. See 6823049. +- (BOOL)_wantsUserAttention +{ + return NO; +} +@end + +namespace WebKit { + +class PluginDestroyDeferrer { +public: + PluginDestroyDeferrer(NetscapePluginInstanceProxy* proxy) + : m_proxy(proxy) + { + m_proxy->willCallPluginFunction(); + } + + ~PluginDestroyDeferrer() + { + bool stopped; + m_proxy->didCallPluginFunction(stopped); + } + +private: + RefPtr<NetscapePluginInstanceProxy> m_proxy; +}; + +typedef HashMap<mach_port_t, NetscapePluginHostProxy*> PluginProxyMap; +static PluginProxyMap& pluginProxyMap() +{ + DEFINE_STATIC_LOCAL(PluginProxyMap, pluginProxyMap, ()); + + return pluginProxyMap; +} + +unsigned NetscapePluginHostProxy::s_processingRequests; + +NetscapePluginHostProxy::NetscapePluginHostProxy(mach_port_t clientPort, mach_port_t pluginHostPort, const ProcessSerialNumber& pluginHostPSN, bool shouldCacheMissingPropertiesAndMethods) + : m_clientPort(clientPort) + , m_portSet(MACH_PORT_NULL) + , m_pluginHostPort(pluginHostPort) + , m_isModal(false) + , m_menuBarIsVisible(true) + , m_fullscreenWindowIsShowing(false) + , m_pluginHostPSN(pluginHostPSN) + , m_shouldCacheMissingPropertiesAndMethods(shouldCacheMissingPropertiesAndMethods) +{ + pluginProxyMap().add(m_clientPort, this); + + // FIXME: We should use libdispatch for this. + CFMachPortContext context = { 0, this, 0, 0, 0 }; + m_deadNameNotificationPort.adoptCF(CFMachPortCreate(0, deadNameNotificationCallback, &context, 0)); + + mach_port_t previous; + mach_port_request_notification(mach_task_self(), pluginHostPort, MACH_NOTIFY_DEAD_NAME, 0, + CFMachPortGetPort(m_deadNameNotificationPort.get()), MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous); + ASSERT(previous == MACH_PORT_NULL); + + RetainPtr<CFRunLoopSourceRef> deathPortSource(AdoptCF, CFMachPortCreateRunLoopSource(0, m_deadNameNotificationPort.get(), 0)); + + CFRunLoopAddSource(CFRunLoopGetCurrent(), deathPortSource.get(), kCFRunLoopDefaultMode); + + m_clientPortSource.adoptCF(WKCreateMIGServerSource((mig_subsystem_t)&WKWebKitPluginClient_subsystem, m_clientPort)); + CFRunLoopAddSource(CFRunLoopGetCurrent(), m_clientPortSource.get(), kCFRunLoopDefaultMode); + CFRunLoopAddSource(CFRunLoopGetCurrent(), m_clientPortSource.get(), (CFStringRef)NSEventTrackingRunLoopMode); +} + +NetscapePluginHostProxy::~NetscapePluginHostProxy() +{ + pluginProxyMap().remove(m_clientPort); + + // Free the port set + if (m_portSet) { + mach_port_extract_member(mach_task_self(), m_clientPort, m_portSet); + mach_port_extract_member(mach_task_self(), CFMachPortGetPort(m_deadNameNotificationPort.get()), m_portSet); + mach_port_destroy(mach_task_self(), m_portSet); + } + + ASSERT(m_clientPortSource); + CFRunLoopSourceInvalidate(m_clientPortSource.get()); + m_clientPortSource = 0; +} + +void NetscapePluginHostProxy::pluginHostDied() +{ + PluginInstanceMap instances; + m_instances.swap(instances); + + PluginInstanceMap::const_iterator end = instances.end(); + for (PluginInstanceMap::const_iterator it = instances.begin(); it != end; ++it) + it->second->pluginHostDied(); + + NetscapePluginHostManager::shared().pluginHostDied(this); + + // The plug-in crashed while its menu bar was hidden. Make sure to show it. + if (!m_menuBarIsVisible) + setMenuBarVisible(true); + + // The plug-in crashed while it had a modal dialog up. + if (m_isModal) + endModal(); + + delete this; +} + +void NetscapePluginHostProxy::addPluginInstance(NetscapePluginInstanceProxy* instance) +{ + ASSERT(!m_instances.contains(instance->pluginID())); + + m_instances.set(instance->pluginID(), instance); +} + +void NetscapePluginHostProxy::removePluginInstance(NetscapePluginInstanceProxy* instance) +{ + ASSERT(m_instances.get(instance->pluginID()) == instance); + + m_instances.remove(instance->pluginID()); +} + +NetscapePluginInstanceProxy* NetscapePluginHostProxy::pluginInstance(uint32_t pluginID) +{ + NetscapePluginInstanceProxy* result = m_instances.get(pluginID).get(); + ASSERT(!result || result->hostProxy() == this); + return result; +} + +void NetscapePluginHostProxy::deadNameNotificationCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) +{ + ASSERT(msg); + ASSERT(static_cast<mach_msg_header_t*>(msg)->msgh_id == MACH_NOTIFY_DEAD_NAME); + + static_cast<NetscapePluginHostProxy*>(info)->pluginHostDied(); +} + +void NetscapePluginHostProxy::setMenuBarVisible(bool visible) +{ + m_menuBarIsVisible = visible; + + [NSMenu setMenuBarVisible:visible]; +} + +void NetscapePluginHostProxy::didEnterFullscreen() const +{ + SetFrontProcess(&m_pluginHostPSN); +} + +void NetscapePluginHostProxy::didExitFullscreen() const +{ + // If the plug-in host is the current application then we should bring ourselves to the front when it exits full-screen mode. + + ProcessSerialNumber frontProcess; + GetFrontProcess(&frontProcess); + Boolean isSameProcess = 0; + SameProcess(&frontProcess, &m_pluginHostPSN, &isSameProcess); + if (!isSameProcess) + return; + + ProcessSerialNumber currentProcess; + GetCurrentProcess(¤tProcess); + SetFrontProcess(¤tProcess); +} + +void NetscapePluginHostProxy::setFullscreenWindowIsShowing(bool isShowing) +{ + if (m_fullscreenWindowIsShowing == isShowing) + return; + + m_fullscreenWindowIsShowing = isShowing; + if (m_fullscreenWindowIsShowing) + didEnterFullscreen(); + else + didExitFullscreen(); + +} + +void NetscapePluginHostProxy::applicationDidBecomeActive() +{ + SetFrontProcess(&m_pluginHostPSN); +} + +void NetscapePluginHostProxy::beginModal() +{ + ASSERT(!m_placeholderWindow); + ASSERT(!m_activationObserver); + + m_placeholderWindow.adoptNS([[WebPlaceholderModalWindow alloc] initWithContentRect:NSMakeRect(0, 0, 1, 1) styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES]); + + m_activationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationWillBecomeActiveNotification object:NSApp queue:nil + usingBlock:^(NSNotification *){ applicationDidBecomeActive(); }]; + + // We need to be able to get the setModal(false) call from the plug-in host. + CFRunLoopAddSource(CFRunLoopGetCurrent(), m_clientPortSource.get(), (CFStringRef)NSModalPanelRunLoopMode); + + [NSApp runModalForWindow:m_placeholderWindow.get()]; + + [m_placeholderWindow.get() orderOut:nil]; + m_placeholderWindow = 0; +} + +void NetscapePluginHostProxy::endModal() +{ + ASSERT(m_placeholderWindow); + ASSERT(m_activationObserver); + + [[NSNotificationCenter defaultCenter] removeObserver:m_activationObserver.get()]; + m_activationObserver = nil; + + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m_clientPortSource.get(), (CFStringRef)NSModalPanelRunLoopMode); + + [NSApp stopModal]; + + // Make ourselves the front process. + ProcessSerialNumber psn; + GetCurrentProcess(&psn); + SetFrontProcess(&psn); +} + + +void NetscapePluginHostProxy::setModal(bool modal) +{ + if (modal == m_isModal) + return; + + m_isModal = modal; + + if (m_isModal) + beginModal(); + else + endModal(); +} + +bool NetscapePluginHostProxy::processRequests() +{ + s_processingRequests++; + + if (!m_portSet) { + mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &m_portSet); + mach_port_insert_member(mach_task_self(), m_clientPort, m_portSet); + mach_port_insert_member(mach_task_self(), CFMachPortGetPort(m_deadNameNotificationPort.get()), m_portSet); + } + + char buffer[4096]; + + mach_msg_header_t* msg = reinterpret_cast<mach_msg_header_t*>(buffer); + + kern_return_t kr = mach_msg(msg, MACH_RCV_MSG, 0, sizeof(buffer), m_portSet, 0, MACH_PORT_NULL); + + if (kr != KERN_SUCCESS) { + LOG_ERROR("Could not receive mach message, error %x", kr); + s_processingRequests--; + return false; + } + + if (msg->msgh_local_port == m_clientPort) { + __ReplyUnion__WKWebKitPluginClient_subsystem reply; + mach_msg_header_t* replyHeader = reinterpret_cast<mach_msg_header_t*>(&reply); + + if (WebKitPluginClient_server(msg, replyHeader) && replyHeader->msgh_remote_port != MACH_PORT_NULL) { + kr = mach_msg(replyHeader, MACH_SEND_MSG, replyHeader->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); + + if (kr != KERN_SUCCESS) { + LOG_ERROR("Could not send mach message, error %x", kr); + s_processingRequests--; + return false; + } + } + + s_processingRequests--; + return true; + } + + if (msg->msgh_local_port == CFMachPortGetPort(m_deadNameNotificationPort.get())) { + ASSERT(msg->msgh_id == MACH_NOTIFY_DEAD_NAME); + pluginHostDied(); + s_processingRequests--; + return false; + } + + ASSERT_NOT_REACHED(); + s_processingRequests--; + return false; +} + +} // namespace WebKit + +using namespace WebKit; + +// Helper class for deallocating data +class DataDeallocator { +public: + DataDeallocator(data_t data, mach_msg_type_number_t dataLength) + : m_data(reinterpret_cast<vm_address_t>(data)) + , m_dataLength(dataLength) + { + } + + ~DataDeallocator() + { + if (!m_data) + return; + + vm_deallocate(mach_task_self(), m_data, m_dataLength); + } + +private: + vm_address_t m_data; + vm_size_t m_dataLength; +}; + +// MiG callbacks +kern_return_t WKPCStatusText(mach_port_t clientPort, uint32_t pluginID, data_t text, mach_msg_type_number_t textCnt) +{ + DataDeallocator deallocator(text, textCnt); + + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + instanceProxy->status(text); + return KERN_SUCCESS; +} + +kern_return_t WKPCLoadURL(mach_port_t clientPort, uint32_t pluginID, data_t url, mach_msg_type_number_t urlLength, data_t target, mach_msg_type_number_t targetLength, + data_t postData, mach_msg_type_number_t postDataLength, uint32_t flags, + uint16_t* outResult, uint32_t* outStreamID) +{ + DataDeallocator urlDeallocator(url, urlLength); + DataDeallocator targetDeallocator(target, targetLength); + DataDeallocator postDataDeallocator(postData, postDataLength); + + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + uint32_t streamID = 0; + NPError result = instanceProxy->loadURL(url, target, postData, postDataLength, static_cast<LoadURLFlags>(flags), streamID); + + *outResult = result; + *outStreamID = streamID; + return KERN_SUCCESS; +} + +kern_return_t WKPCCancelLoadURL(mach_port_t clientPort, uint32_t pluginID, uint32_t streamID, int16_t reason) +{ + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + if (!instanceProxy->cancelStreamLoad(streamID, reason)) + return KERN_FAILURE; + + return KERN_SUCCESS; +} + +kern_return_t WKPCInvalidateRect(mach_port_t clientPort, uint32_t pluginID, double x, double y, double width, double height) +{ + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_SUCCESS; + + if (!hostProxy->isProcessingRequests()) { + if (NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID)) + instanceProxy->invalidateRect(x, y, width, height); + return KERN_SUCCESS; + } + + // Defer the work + CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopDefaultMode, ^{ + if (NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort)) { + if (NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID)) + instanceProxy->invalidateRect(x, y, width, height); + } + }); + + return KERN_SUCCESS; +} + +kern_return_t WKPCGetScriptableNPObjectReply(mach_port_t clientPort, uint32_t pluginID, uint32_t requestID, uint32_t objectID) +{ + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + instanceProxy->setCurrentReply(requestID, new NetscapePluginInstanceProxy::GetScriptableNPObjectReply(objectID)); + return KERN_SUCCESS; +} + +kern_return_t WKPCBooleanReply(mach_port_t clientPort, uint32_t pluginID, uint32_t requestID, boolean_t result) +{ + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + instanceProxy->setCurrentReply(requestID, new NetscapePluginInstanceProxy::BooleanReply(result)); + return KERN_SUCCESS; +} + +kern_return_t WKPCBooleanAndDataReply(mach_port_t clientPort, uint32_t pluginID, uint32_t requestID, boolean_t returnValue, data_t resultData, mach_msg_type_number_t resultLength) +{ + DataDeallocator deallocator(resultData, resultLength); + + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + RetainPtr<CFDataRef> result(AdoptCF, CFDataCreate(0, reinterpret_cast<UInt8*>(resultData), resultLength)); + instanceProxy->setCurrentReply(requestID, new NetscapePluginInstanceProxy::BooleanAndDataReply(returnValue, result)); + + return KERN_SUCCESS; +} + +kern_return_t WKPCInstantiatePluginReply(mach_port_t clientPort, uint32_t pluginID, uint32_t requestID, kern_return_t result, uint32_t renderContextID, uint32_t rendererType) +{ + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + instanceProxy->setCurrentReply(requestID, new NetscapePluginInstanceProxy::InstantiatePluginReply(result, renderContextID, static_cast<RendererType>(rendererType))); + return KERN_SUCCESS; +} + +kern_return_t WKPCGetWindowNPObject(mach_port_t clientPort, uint32_t pluginID, uint32_t* outObjectID) +{ + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + uint32_t objectID; + if (!instanceProxy->getWindowNPObject(objectID)) + return KERN_FAILURE; + + *outObjectID = objectID; + return KERN_SUCCESS; +} + +kern_return_t WKPCGetPluginElementNPObject(mach_port_t clientPort, uint32_t pluginID, uint32_t* outObjectID) +{ + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + uint32_t objectID; + if (!instanceProxy->getPluginElementNPObject(objectID)) + return KERN_FAILURE; + + *outObjectID = objectID; + return KERN_SUCCESS; +} + +kern_return_t WKPCForgetBrowserObject(mach_port_t clientPort, uint32_t pluginID, uint32_t objectID) +{ + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + return instanceProxy->forgetBrowserObjectID(objectID) ? KERN_SUCCESS : KERN_FAILURE; +} + +kern_return_t WKPCEvaluate(mach_port_t clientPort, uint32_t pluginID, uint32_t requestID, uint32_t objectID, data_t scriptData, mach_msg_type_number_t scriptLength, boolean_t allowPopups) +{ + DataDeallocator deallocator(scriptData, scriptLength); + + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + PluginDestroyDeferrer deferrer(instanceProxy); + + String script = String::fromUTF8WithLatin1Fallback(scriptData, scriptLength); + + data_t resultData = 0; + mach_msg_type_number_t resultLength = 0; + boolean_t returnValue = instanceProxy->evaluate(objectID, script, resultData, resultLength, allowPopups); + + hostProxy = instanceProxy->hostProxy(); + if (!hostProxy) + return KERN_FAILURE; + + _WKPHBooleanAndDataReply(hostProxy->port(), instanceProxy->pluginID(), requestID, returnValue, resultData, resultLength); + if (resultData) + mig_deallocate(reinterpret_cast<vm_address_t>(resultData), resultLength); + + return KERN_SUCCESS; +} + +kern_return_t WKPCGetStringIdentifier(mach_port_t clientPort, data_t name, mach_msg_type_number_t nameCnt, uint64_t* identifier) +{ + DataDeallocator deallocator(name, nameCnt); + + COMPILE_ASSERT(sizeof(*identifier) == sizeof(IdentifierRep*), identifier_sizes); + + *identifier = reinterpret_cast<uint64_t>(IdentifierRep::get(name)); + return KERN_SUCCESS; +} + +kern_return_t WKPCGetIntIdentifier(mach_port_t clientPort, int32_t value, uint64_t* identifier) +{ + COMPILE_ASSERT(sizeof(*identifier) == sizeof(NPIdentifier), identifier_sizes); + + *identifier = reinterpret_cast<uint64_t>(IdentifierRep::get(value)); + return KERN_SUCCESS; +} + +static Identifier identifierFromIdentifierRep(IdentifierRep* identifier) +{ + ASSERT(IdentifierRep::isValid(identifier)); + ASSERT(identifier->isString()); + + const char* str = identifier->string(); + return Identifier(JSDOMWindow::commonJSGlobalData(), stringToUString(String::fromUTF8WithLatin1Fallback(str, strlen(str)))); +} + +kern_return_t WKPCInvoke(mach_port_t clientPort, uint32_t pluginID, uint32_t requestID, uint32_t objectID, uint64_t serverIdentifier, + data_t argumentsData, mach_msg_type_number_t argumentsLength) +{ + DataDeallocator deallocator(argumentsData, argumentsLength); + + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + PluginDestroyDeferrer deferrer(instanceProxy); + + IdentifierRep* identifier = reinterpret_cast<IdentifierRep*>(serverIdentifier); + if (!IdentifierRep::isValid(identifier)) + return KERN_FAILURE; + + Identifier methodNameIdentifier = identifierFromIdentifierRep(identifier); + + data_t resultData = 0; + mach_msg_type_number_t resultLength = 0; + boolean_t returnValue = instanceProxy->invoke(objectID, methodNameIdentifier, argumentsData, argumentsLength, resultData, resultLength); + + hostProxy = instanceProxy->hostProxy(); + if (!hostProxy) + return KERN_FAILURE; + + _WKPHBooleanAndDataReply(hostProxy->port(), instanceProxy->pluginID(), requestID, returnValue, resultData, resultLength); + if (resultData) + mig_deallocate(reinterpret_cast<vm_address_t>(resultData), resultLength); + + return KERN_SUCCESS; +} + +kern_return_t WKPCInvokeDefault(mach_port_t clientPort, uint32_t pluginID, uint32_t requestID, uint32_t objectID, + data_t argumentsData, mach_msg_type_number_t argumentsLength) +{ + DataDeallocator deallocator(argumentsData, argumentsLength); + + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + PluginDestroyDeferrer deferrer(instanceProxy); + + data_t resultData = 0; + mach_msg_type_number_t resultLength = 0; + boolean_t returnValue = instanceProxy->invokeDefault(objectID, argumentsData, argumentsLength, resultData, resultLength); + + hostProxy = instanceProxy->hostProxy(); + if (!hostProxy) + return KERN_FAILURE; + + _WKPHBooleanAndDataReply(hostProxy->port(), instanceProxy->pluginID(), requestID, returnValue, resultData, resultLength); + if (resultData) + mig_deallocate(reinterpret_cast<vm_address_t>(resultData), resultLength); + + return KERN_SUCCESS; +} + +kern_return_t WKPCConstruct(mach_port_t clientPort, uint32_t pluginID, uint32_t objectID, + data_t argumentsData, mach_msg_type_number_t argumentsLength, + boolean_t* returnValue, data_t* resultData, mach_msg_type_number_t* resultLength) +{ + DataDeallocator deallocator(argumentsData, argumentsLength); + + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + PluginDestroyDeferrer deferrer(instanceProxy); + + *returnValue = instanceProxy->construct(objectID, argumentsData, argumentsLength, *resultData, *resultLength); + + return KERN_SUCCESS; +} + +kern_return_t WKPCGetProperty(mach_port_t clientPort, uint32_t pluginID, uint32_t requestID, uint32_t objectID, uint64_t serverIdentifier) +{ + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + IdentifierRep* identifier = reinterpret_cast<IdentifierRep*>(serverIdentifier); + if (!IdentifierRep::isValid(identifier)) + return KERN_FAILURE; + + PluginDestroyDeferrer deferrer(instanceProxy); + + data_t resultData = 0; + mach_msg_type_number_t resultLength = 0; + boolean_t returnValue; + + if (identifier->isString()) { + Identifier propertyNameIdentifier = identifierFromIdentifierRep(identifier); + returnValue = instanceProxy->getProperty(objectID, propertyNameIdentifier, resultData, resultLength); + } else + returnValue = instanceProxy->setProperty(objectID, identifier->number(), resultData, resultLength); + + hostProxy = instanceProxy->hostProxy(); + if (!hostProxy) + return KERN_FAILURE; + + _WKPHBooleanAndDataReply(hostProxy->port(), instanceProxy->pluginID(), requestID, returnValue, resultData, resultLength); + if (resultData) + mig_deallocate(reinterpret_cast<vm_address_t>(resultData), resultLength); + + return KERN_SUCCESS; +} + +kern_return_t WKPCSetProperty(mach_port_t clientPort, uint32_t pluginID, uint32_t requestID, uint32_t objectID, uint64_t serverIdentifier, data_t valueData, mach_msg_type_number_t valueLength) +{ + DataDeallocator deallocator(valueData, valueLength); + + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + PluginDestroyDeferrer deferrer(instanceProxy); + + IdentifierRep* identifier = reinterpret_cast<IdentifierRep*>(serverIdentifier); + if (!IdentifierRep::isValid(identifier)) + return KERN_FAILURE; + + bool result; + if (identifier->isString()) { + Identifier propertyNameIdentifier = identifierFromIdentifierRep(identifier); + result = instanceProxy->setProperty(objectID, propertyNameIdentifier, valueData, valueLength); + } else + result = instanceProxy->setProperty(objectID, identifier->number(), valueData, valueLength); + + hostProxy = instanceProxy->hostProxy(); + if (!hostProxy) + return KERN_FAILURE; + + _WKPHBooleanReply(hostProxy->port(), instanceProxy->pluginID(), requestID, result); + + return KERN_SUCCESS; +} + +kern_return_t WKPCRemoveProperty(mach_port_t clientPort, uint32_t pluginID, uint32_t requestID, uint32_t objectID, uint64_t serverIdentifier) +{ + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + PluginDestroyDeferrer deferrer(instanceProxy); + + IdentifierRep* identifier = reinterpret_cast<IdentifierRep*>(serverIdentifier); + if (!IdentifierRep::isValid(identifier)) + return KERN_FAILURE; + + bool result; + if (identifier->isString()) { + Identifier propertyNameIdentifier = identifierFromIdentifierRep(identifier); + result = instanceProxy->removeProperty(objectID, propertyNameIdentifier); + } else + result = instanceProxy->removeProperty(objectID, identifier->number()); + + hostProxy = instanceProxy->hostProxy(); + if (!hostProxy) + return KERN_FAILURE; + + _WKPHBooleanReply(hostProxy->port(), instanceProxy->pluginID(), requestID, result); + + return KERN_SUCCESS; +} + +kern_return_t WKPCHasProperty(mach_port_t clientPort, uint32_t pluginID, uint32_t requestID, uint32_t objectID, uint64_t serverIdentifier) +{ + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + PluginDestroyDeferrer deferrer(instanceProxy); + + IdentifierRep* identifier = reinterpret_cast<IdentifierRep*>(serverIdentifier); + if (!IdentifierRep::isValid(identifier)) + return KERN_FAILURE; + + boolean_t returnValue; + if (identifier->isString()) { + Identifier propertyNameIdentifier = identifierFromIdentifierRep(identifier); + returnValue = instanceProxy->hasProperty(objectID, propertyNameIdentifier); + } else + returnValue = instanceProxy->hasProperty(objectID, identifier->number()); + + hostProxy = instanceProxy->hostProxy(); + if (!hostProxy) + return KERN_FAILURE; + + _WKPHBooleanReply(hostProxy->port(), instanceProxy->pluginID(), requestID, returnValue); + + return KERN_SUCCESS; +} + +kern_return_t WKPCHasMethod(mach_port_t clientPort, uint32_t pluginID, uint32_t requestID, uint32_t objectID, uint64_t serverIdentifier) +{ + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + PluginDestroyDeferrer deferrer(instanceProxy); + + IdentifierRep* identifier = reinterpret_cast<IdentifierRep*>(serverIdentifier); + if (!IdentifierRep::isValid(identifier)) + return KERN_FAILURE; + + Identifier methodNameIdentifier = identifierFromIdentifierRep(identifier); + boolean_t returnValue = instanceProxy->hasMethod(objectID, methodNameIdentifier); + + hostProxy = instanceProxy->hostProxy(); + if (!hostProxy) + return KERN_FAILURE; + + _WKPHBooleanReply(hostProxy->port(), instanceProxy->pluginID(), requestID, returnValue); + + return KERN_SUCCESS; +} + +kern_return_t WKPCIdentifierInfo(mach_port_t clientPort, uint64_t serverIdentifier, data_t* infoData, mach_msg_type_number_t* infoLength) +{ + IdentifierRep* identifier = reinterpret_cast<IdentifierRep*>(serverIdentifier); + if (!IdentifierRep::isValid(identifier)) + return KERN_FAILURE; + + id info; + if (identifier->isString()) { + const char* str = identifier->string(); + info = [NSData dataWithBytesNoCopy:(void*)str length:strlen(str) freeWhenDone:NO]; + } else + info = [NSNumber numberWithInt:identifier->number()]; + + RetainPtr<NSData*> data = [NSPropertyListSerialization dataFromPropertyList:info format:NSPropertyListBinaryFormat_v1_0 errorDescription:0]; + ASSERT(data); + + *infoLength = [data.get() length]; + mig_allocate(reinterpret_cast<vm_address_t*>(infoData), *infoLength); + + memcpy(*infoData, [data.get() bytes], *infoLength); + + return KERN_SUCCESS; +} + +kern_return_t WKPCEnumerate(mach_port_t clientPort, uint32_t pluginID, uint32_t requestID, uint32_t objectID) +{ + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + data_t resultData = 0; + mach_msg_type_number_t resultLength = 0; + boolean_t returnValue = instanceProxy->enumerate(objectID, resultData, resultLength); + + hostProxy = instanceProxy->hostProxy(); + if (!hostProxy) + return KERN_FAILURE; + + _WKPHBooleanAndDataReply(hostProxy->port(), instanceProxy->pluginID(), requestID, returnValue, resultData, resultLength); + + if (resultData) + mig_deallocate(reinterpret_cast<vm_address_t>(resultData), resultLength); + + return KERN_SUCCESS; +} + +kern_return_t WKPCSetMenuBarVisible(mach_port_t clientPort, boolean_t menuBarVisible) +{ + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + hostProxy->setMenuBarVisible(menuBarVisible); + + return KERN_SUCCESS; +} + +kern_return_t WKPCSetFullscreenWindowIsShowing(mach_port_t clientPort, boolean_t fullscreenWindowIsShowing) +{ + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + hostProxy->setFullscreenWindowIsShowing(fullscreenWindowIsShowing); + + return KERN_SUCCESS; +} + +kern_return_t WKPCSetModal(mach_port_t clientPort, boolean_t modal) +{ + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + if (!hostProxy->isProcessingRequests()) { + hostProxy->setModal(modal); + return KERN_SUCCESS; + } + + // Defer the work + CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopDefaultMode, ^{ + if (NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort)) + hostProxy->setModal(modal); + }); + + return KERN_SUCCESS; +} + +kern_return_t WKPCGetCookies(mach_port_t clientPort, uint32_t pluginID, + data_t urlData, mach_msg_type_number_t urlLength, + boolean_t* returnValue, data_t* cookiesData, mach_msg_type_number_t* cookiesLength) +{ + *cookiesData = 0; + *cookiesLength = 0; + + DataDeallocator deallocator(urlData, urlLength); + + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + *returnValue = instanceProxy->getCookies(urlData, urlLength, *cookiesData, *cookiesLength); + + return KERN_SUCCESS; +} + +kern_return_t WKPCGetProxy(mach_port_t clientPort, uint32_t pluginID, + data_t urlData, mach_msg_type_number_t urlLength, + boolean_t* returnValue, data_t* proxyData, mach_msg_type_number_t* proxyLength) +{ + *proxyData = 0; + *proxyLength = 0; + + DataDeallocator deallocator(urlData, urlLength); + + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + *returnValue = instanceProxy->getProxy(urlData, urlLength, *proxyData, *proxyLength); + + return KERN_SUCCESS; +} + +kern_return_t WKPCSetCookies(mach_port_t clientPort, uint32_t pluginID, + data_t urlData, mach_msg_type_number_t urlLength, + data_t cookiesData, mach_msg_type_number_t cookiesLength, + boolean_t* returnValue) +{ + DataDeallocator urlDeallocator(urlData, urlLength); + DataDeallocator cookiesDeallocator(cookiesData, cookiesLength); + + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + *returnValue = instanceProxy->setCookies(urlData, urlLength, cookiesData, cookiesLength); + return KERN_SUCCESS; +} + +kern_return_t WKPCGetAuthenticationInfo(mach_port_t clientPort, uint32_t pluginID, + data_t protocolData, mach_msg_type_number_t protocolLength, + data_t hostData, mach_msg_type_number_t hostLength, + uint32_t port, + data_t schemeData, mach_msg_type_number_t schemeLength, + data_t realmData, mach_msg_type_number_t realmLength, + boolean_t* returnValue, + data_t* usernameData, mach_msg_type_number_t *usernameLength, + data_t* passwordData, mach_msg_type_number_t *passwordLength) +{ + DataDeallocator protocolDeallocator(protocolData, protocolLength); + DataDeallocator hostDeallocator(hostData, hostLength); + DataDeallocator schemeDeallocator(schemeData, schemeLength); + DataDeallocator realmDeallocator(realmData, realmLength); + + *usernameData = 0; + *usernameLength = 0; + *passwordData = 0; + *passwordLength = 0; + + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + *returnValue = instanceProxy->getAuthenticationInfo(protocolData, hostData, port, schemeData, realmData, *usernameData, *usernameLength, *passwordData, *passwordLength); + + return KERN_SUCCESS; +} + +kern_return_t WKPCConvertPoint(mach_port_t clientPort, uint32_t pluginID, + double sourceX, double sourceY, uint32_t sourceSpace, + uint32_t destSpace, boolean_t *returnValue, double *destX, double *destY) +{ + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + *returnValue = instanceProxy->convertPoint(sourceX, sourceY, static_cast<NPCoordinateSpace>(sourceSpace), + *destX, *destY, static_cast<NPCoordinateSpace>(destSpace)); + return KERN_SUCCESS; +} + +kern_return_t WKPCCheckIfAllowedToLoadURL(mach_port_t clientPort, uint32_t pluginID, data_t urlData, mach_msg_type_number_t urlLength, + data_t targetData, mach_msg_type_number_t targetLength, uint32_t *checkID) +{ + DataDeallocator urlDeallocator(urlData, urlLength); + DataDeallocator targetDeallocator(targetData, targetLength); + + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + *checkID = instanceProxy->checkIfAllowedToLoadURL(urlData, targetData); + return KERN_SUCCESS; +} + +kern_return_t WKPCCancelCheckIfAllowedToLoadURL(mach_port_t clientPort, uint32_t pluginID, uint32_t checkID) +{ + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + instanceProxy->cancelCheckIfAllowedToLoadURL(checkID); + return KERN_SUCCESS; +} + +kern_return_t WKPCResolveURL(mach_port_t clientPort, uint32_t pluginID, data_t urlData, mach_msg_type_number_t urlLength, + data_t targetData, mach_msg_type_number_t targetLength, + data_t *resolvedURLData, mach_msg_type_number_t *resolvedURLLength) +{ + DataDeallocator urlDeallocator(urlData, urlLength); + DataDeallocator targetDeallocator(targetData, targetLength); + + *resolvedURLData = 0; + *resolvedURLLength = 0; + + NetscapePluginHostProxy* hostProxy = pluginProxyMap().get(clientPort); + if (!hostProxy) + return KERN_FAILURE; + + NetscapePluginInstanceProxy* instanceProxy = hostProxy->pluginInstance(pluginID); + if (!instanceProxy) + return KERN_FAILURE; + + instanceProxy->resolveURL(urlData, targetData, *resolvedURLData, *resolvedURLLength); + return KERN_SUCCESS; +} + +kern_return_t WKPCSetException(mach_port_t clientPort, data_t message, mach_msg_type_number_t messageCnt) +{ + DataDeallocator deallocator(message, messageCnt); + + string str(message, messageCnt); + NetscapePluginInstanceProxy::setGlobalException(str.c_str()); + + return KERN_SUCCESS; +} + +#endif // USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API) diff --git a/Source/WebKit/mac/Plugins/Hosted/NetscapePluginInstanceProxy.h b/Source/WebKit/mac/Plugins/Hosted/NetscapePluginInstanceProxy.h new file mode 100644 index 0000000..bf36fbd --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/NetscapePluginInstanceProxy.h @@ -0,0 +1,374 @@ +/* + * Copyright (C) 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. + * + * 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. + */ + +#if USE(PLUGIN_HOST_PROCESS) + +#ifndef NetscapePluginInstanceProxy_h +#define NetscapePluginInstanceProxy_h + +#include <JavaScriptCore/JSGlobalData.h> +#include <JavaScriptCore/Strong.h> +#include <WebCore/Timer.h> +#include <WebKit/npapi.h> +#include <wtf/Deque.h> +#include <wtf/Forward.h> +#include <wtf/HashMap.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RetainPtr.h> +#include "WebKitPluginHostTypes.h" + +namespace JSC { + namespace Bindings { + class Instance; + class RootObject; + } + class ArgList; +} +@class WebHostedNetscapePluginView; +@class WebFrame; + +namespace WebKit { + +class HostedNetscapePluginStream; +class NetscapePluginHostProxy; +class PluginRequest; +class ProxyInstance; + +class NetscapePluginInstanceProxy : public RefCounted<NetscapePluginInstanceProxy> { +public: + static PassRefPtr<NetscapePluginInstanceProxy> create(NetscapePluginHostProxy*, WebHostedNetscapePluginView *, bool fullFramePlugin); + ~NetscapePluginInstanceProxy(); + + uint32_t pluginID() const + { + ASSERT(m_pluginID); + + return m_pluginID; + } + uint32_t renderContextID() const { ASSERT(fastMallocSize(this)); return m_renderContextID; } + void setRenderContextID(uint32_t renderContextID) { m_renderContextID = renderContextID; } + + RendererType rendererType() const { return m_rendererType; } + void setRendererType(RendererType rendererType) { m_rendererType = rendererType; } + + WebHostedNetscapePluginView *pluginView() const { ASSERT(fastMallocSize(this)); return m_pluginView; } + NetscapePluginHostProxy* hostProxy() const { ASSERT(fastMallocSize(this)); return m_pluginHostProxy; } + + bool cancelStreamLoad(uint32_t streamID, NPReason); + void disconnectStream(HostedNetscapePluginStream*); + + void setManualStream(PassRefPtr<HostedNetscapePluginStream>); + HostedNetscapePluginStream* manualStream() const { return m_manualStream.get(); } + + void pluginHostDied(); + + void resize(NSRect size, NSRect clipRect); + void destroy(); + void focusChanged(bool hasFocus); + void windowFocusChanged(bool hasFocus); + void windowFrameChanged(NSRect frame); + + void mouseEvent(NSView *pluginView, NSEvent *, NPCocoaEventType); + void keyEvent(NSView *pluginView, NSEvent *, NPCocoaEventType); + void insertText(NSString *); + bool wheelEvent(NSView *pluginView, NSEvent *); + void syntheticKeyDownWithCommandModifier(int keyCode, char character); + void flagsChanged(NSEvent *); + void print(CGContextRef, unsigned width, unsigned height); + void snapshot(CGContextRef, unsigned width, unsigned height); + + void startTimers(bool throttleTimers); + void stopTimers(); + + void invalidateRect(double x, double y, double width, double height); + + // NPRuntime + bool getWindowNPObject(uint32_t& objectID); + bool getPluginElementNPObject(uint32_t& objectID); + bool forgetBrowserObjectID(uint32_t objectID); // Will fail if the ID is being sent to plug-in right now (i.e., retain/release calls aren't balanced). + + bool evaluate(uint32_t objectID, const WTF::String& script, data_t& resultData, mach_msg_type_number_t& resultLength, bool allowPopups); + bool invoke(uint32_t objectID, const JSC::Identifier& methodName, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength); + bool invokeDefault(uint32_t objectID, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength); + bool construct(uint32_t objectID, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength); + bool enumerate(uint32_t objectID, data_t& resultData, mach_msg_type_number_t& resultLength); + + bool getProperty(uint32_t objectID, const JSC::Identifier& propertyName, data_t &resultData, mach_msg_type_number_t& resultLength); + bool getProperty(uint32_t objectID, unsigned propertyName, data_t &resultData, mach_msg_type_number_t& resultLength); + bool setProperty(uint32_t objectID, const JSC::Identifier& propertyName, data_t valueData, mach_msg_type_number_t valueLength); + bool setProperty(uint32_t objectID, unsigned propertyName, data_t valueData, mach_msg_type_number_t valueLength); + bool removeProperty(uint32_t objectID, const JSC::Identifier& propertyName); + bool removeProperty(uint32_t objectID, unsigned propertyName); + bool hasProperty(uint32_t objectID, const JSC::Identifier& propertyName); + bool hasProperty(uint32_t objectID, unsigned propertyName); + bool hasMethod(uint32_t objectID, const JSC::Identifier& methodName); + + void status(const char* message); + NPError loadURL(const char* url, const char* target, const char* postData, uint32_t postDataLength, LoadURLFlags, uint32_t& requestID); + + bool getCookies(data_t urlData, mach_msg_type_number_t urlLength, data_t& cookiesData, mach_msg_type_number_t& cookiesLength); + bool setCookies(data_t urlData, mach_msg_type_number_t urlLength, data_t cookiesData, mach_msg_type_number_t cookiesLength); + + bool getProxy(data_t urlData, mach_msg_type_number_t urlLength, data_t& proxyData, mach_msg_type_number_t& proxyLength); + bool getAuthenticationInfo(data_t protocolData, data_t hostData, uint32_t port, data_t schemeData, data_t realmData, + data_t& usernameData, mach_msg_type_number_t& usernameLength, data_t& passwordData, mach_msg_type_number_t& passwordLength); + bool convertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace, + double& destX, double& destY, NPCoordinateSpace destSpace); + + PassRefPtr<JSC::Bindings::Instance> createBindingsInstance(PassRefPtr<JSC::Bindings::RootObject>); + RetainPtr<NSData *> marshalValues(JSC::ExecState*, const JSC::ArgList& args); + void marshalValue(JSC::ExecState*, JSC::JSValue, data_t& resultData, mach_msg_type_number_t& resultLength); + JSC::JSValue demarshalValue(JSC::ExecState*, const char* valueData, mach_msg_type_number_t valueLength); + + // No-op if the value does not contain a local object. + void retainLocalObject(JSC::JSValue); + void releaseLocalObject(JSC::JSValue); + + void addInstance(ProxyInstance*); + void removeInstance(ProxyInstance*); + + void cleanup(); + void invalidate(); + + void willCallPluginFunction(); + void didCallPluginFunction(bool& stopped); + bool shouldStop(); + + uint32_t nextRequestID(); + + uint32_t checkIfAllowedToLoadURL(const char* url, const char* target); + void cancelCheckIfAllowedToLoadURL(uint32_t checkID); + void checkIfAllowedToLoadURLResult(uint32_t checkID, bool allowed); + + void resolveURL(const char* url, const char* target, data_t& resolvedURLData, mach_msg_type_number_t& resolvedURLLength); + + void didDraw(); + void privateBrowsingModeDidChange(bool isPrivateBrowsingEnabled); + + static void setGlobalException(const WTF::String&); + static void moveGlobalExceptionToExecState(JSC::ExecState*); + + // Reply structs + struct Reply { + enum Type { + InstantiatePlugin, + GetScriptableNPObject, + BooleanAndData, + Boolean + }; + + Reply(Type type) + : m_type(type) + { + } + + virtual ~Reply() { } + + Type m_type; + }; + + struct InstantiatePluginReply : public Reply { + static const int ReplyType = InstantiatePlugin; + + InstantiatePluginReply(kern_return_t resultCode, uint32_t renderContextID, RendererType rendererType) + : Reply(InstantiatePlugin) + , m_resultCode(resultCode) + , m_renderContextID(renderContextID) + , m_rendererType(rendererType) + { + } + + kern_return_t m_resultCode; + uint32_t m_renderContextID; + RendererType m_rendererType; + }; + + struct GetScriptableNPObjectReply : public Reply { + static const Reply::Type ReplyType = GetScriptableNPObject; + + GetScriptableNPObjectReply(uint32_t objectID) + : Reply(ReplyType) + , m_objectID(objectID) + { + } + + uint32_t m_objectID; + }; + + struct BooleanReply : public Reply { + static const Reply::Type ReplyType = Boolean; + + BooleanReply(boolean_t result) + : Reply(ReplyType) + , m_result(result) + { + } + + boolean_t m_result; + }; + + struct BooleanAndDataReply : public Reply { + static const Reply::Type ReplyType = BooleanAndData; + + BooleanAndDataReply(boolean_t returnValue, RetainPtr<CFDataRef> result) + : Reply(ReplyType) + , m_returnValue(returnValue) + , m_result(result) + { + } + + boolean_t m_returnValue; + RetainPtr<CFDataRef> m_result; + }; + + void setCurrentReply(uint32_t requestID, Reply* reply) + { + ASSERT(!m_replies.contains(requestID)); + m_replies.set(requestID, reply); + } + + template <typename T> + std::auto_ptr<T> waitForReply(uint32_t requestID) + { + RefPtr<NetscapePluginInstanceProxy> protect(this); // Plug-in host may crash while we are waiting for reply, releasing all instances to the instance proxy. + + willCallPluginFunction(); + m_waitingForReply = true; + + Reply* reply = processRequestsAndWaitForReply(requestID); + if (reply) + ASSERT(reply->m_type == T::ReplyType); + + m_waitingForReply = false; + + bool stopped = false; + didCallPluginFunction(stopped); + if (stopped) { + // The instance proxy may have been deleted from didCallPluginFunction(), so a null reply needs to be returned. + delete static_cast<T*>(reply); + return std::auto_ptr<T>(); + } + + return std::auto_ptr<T>(static_cast<T*>(reply)); + } + + void webFrameDidFinishLoadWithReason(WebFrame*, NPReason); + +private: + NetscapePluginInstanceProxy(NetscapePluginHostProxy*, WebHostedNetscapePluginView*, bool fullFramePlugin); + + NPError loadRequest(NSURLRequest*, const char* cTarget, bool currentEventIsUserGesture, uint32_t& streamID); + + class PluginRequest; + void performRequest(PluginRequest*); + void evaluateJavaScript(PluginRequest*); + + void stopAllStreams(); + Reply* processRequestsAndWaitForReply(uint32_t requestID); + + NetscapePluginHostProxy* m_pluginHostProxy; + WebHostedNetscapePluginView *m_pluginView; + + void requestTimerFired(WebCore::Timer<NetscapePluginInstanceProxy>*); + WebCore::Timer<NetscapePluginInstanceProxy> m_requestTimer; + Deque<RefPtr<PluginRequest> > m_pluginRequests; + + HashMap<uint32_t, RefPtr<HostedNetscapePluginStream> > m_streams; + + uint32_t m_currentURLRequestID; + + uint32_t m_pluginID; + uint32_t m_renderContextID; + RendererType m_rendererType; + + bool m_waitingForReply; + HashMap<uint32_t, Reply*> m_replies; + + // NPRuntime + + void addValueToArray(NSMutableArray *, JSC::ExecState* exec, JSC::JSValue value); + + bool demarshalValueFromArray(JSC::ExecState*, NSArray *array, NSUInteger& index, JSC::JSValue& result); + void demarshalValues(JSC::ExecState*, data_t valuesData, mach_msg_type_number_t valuesLength, JSC::MarkedArgumentBuffer& result); + + class LocalObjectMap { + WTF_MAKE_NONCOPYABLE(LocalObjectMap); + public: + LocalObjectMap(); + ~LocalObjectMap(); + uint32_t idForObject(JSC::JSGlobalData&, JSC::JSObject*); + void retain(JSC::JSObject*); + void release(JSC::JSObject*); + void clear(); + bool forget(uint32_t); + bool contains(uint32_t) const; + JSC::JSObject* get(uint32_t) const; + + private: + HashMap<uint32_t, JSC::Strong<JSC::JSObject> > m_idToJSObjectMap; + // The pair consists of object ID and a reference count. One reference belongs to remote plug-in, + // and the proxy will add transient references for arguments that are being sent out. + HashMap<JSC::JSObject*, pair<uint32_t, uint32_t> > m_jsObjectToIDMap; + uint32_t m_objectIDCounter; + }; + + LocalObjectMap m_localObjects; + + typedef HashSet<ProxyInstance*> ProxyInstanceSet; + ProxyInstanceSet m_instances; + + uint32_t m_urlCheckCounter; + typedef HashMap<uint32_t, RetainPtr<id> > URLCheckMap; + URLCheckMap m_urlChecks; + + unsigned m_pluginFunctionCallDepth; + bool m_shouldStopSoon; + uint32_t m_currentRequestID; + + // All NPRuntime functions will return false when destroying a plug-in. This is necessary because there may be unhandled messages waiting, + // and spinning in processRequests() will unexpectedly execute them from inside destroy(). That's not a good time to execute arbitrary JavaScript, + // since both loading and rendering data structures may be in inconsistent state. + // This suppresses calls from all plug-ins, even those in different pages, since JS might affect the frame with plug-in that's being stopped. + // + // FIXME: Plug-ins can execute arbitrary JS from destroy() in same process case, and other browsers also support that. + // A better fix may be to make sure that unrelated messages are postponed until after destroy() returns. + // Another possible fix may be to send destroy message at a time when internal structures are consistent. + // + // FIXME: We lack similar message suppression in other cases - resize() is also triggered by layout, so executing arbitrary JS is also problematic. + static bool m_inDestroy; + + bool m_pluginIsWaitingForDraw; + + RefPtr<HostedNetscapePluginStream> m_manualStream; + + typedef HashMap<WebFrame*, RefPtr<PluginRequest> > FrameLoadMap; + FrameLoadMap m_pendingFrameLoads; +}; + +} // namespace WebKit + +#endif // NetscapePluginInstanceProxy_h +#endif // USE(PLUGIN_HOST_PROCESS) diff --git a/Source/WebKit/mac/Plugins/Hosted/NetscapePluginInstanceProxy.mm b/Source/WebKit/mac/Plugins/Hosted/NetscapePluginInstanceProxy.mm new file mode 100644 index 0000000..63b0899 --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/NetscapePluginInstanceProxy.mm @@ -0,0 +1,1684 @@ +/* + * Copyright (C) 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. + * + * 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. + */ + +#if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API) + +#import "NetscapePluginInstanceProxy.h" + +#import "HostedNetscapePluginStream.h" +#import "NetscapePluginHostProxy.h" +#import "ProxyInstance.h" +#import "ProxyRuntimeObject.h" +#import "WebDataSourceInternal.h" +#import "WebFrameInternal.h" +#import "WebHostedNetscapePluginView.h" +#import "WebKitNSStringExtras.h" +#import "WebNSDataExtras.h" +#import "WebNSURLExtras.h" +#import "WebPluginRequest.h" +#import "WebUIDelegate.h" +#import "WebUIDelegatePrivate.h" +#import "WebViewInternal.h" +#import <JavaScriptCore/Error.h> +#import <JavaScriptCore/JSLock.h> +#import <JavaScriptCore/PropertyNameArray.h> +#import <WebCore/CookieJar.h> +#import <WebCore/DocumentLoader.h> +#import <WebCore/Frame.h> +#import <WebCore/FrameLoader.h> +#import <WebCore/FrameTree.h> +#import <WebCore/KURL.h> +#import <WebCore/ProxyServer.h> +#import <WebCore/SecurityOrigin.h> +#import <WebCore/ScriptController.h> +#import <WebCore/ScriptValue.h> +#import <WebCore/StringSourceProvider.h> +#import <WebCore/npruntime_impl.h> +#import <WebCore/runtime_object.h> +#import <WebKitSystemInterface.h> +#import <mach/mach.h> +#import <utility> +#import <wtf/RefCountedLeakCounter.h> +#import <wtf/text/CString.h> + +extern "C" { +#import "WebKitPluginClientServer.h" +#import "WebKitPluginHost.h" +} + +using namespace JSC; +using namespace JSC::Bindings; +using namespace std; +using namespace WebCore; + +namespace WebKit { + +class NetscapePluginInstanceProxy::PluginRequest : public RefCounted<NetscapePluginInstanceProxy::PluginRequest> { +public: + static PassRefPtr<PluginRequest> create(uint32_t requestID, NSURLRequest* request, NSString* frameName, bool allowPopups) + { + return adoptRef(new PluginRequest(requestID, request, frameName, allowPopups)); + } + + uint32_t requestID() const { return m_requestID; } + NSURLRequest* request() const { return m_request.get(); } + NSString* frameName() const { return m_frameName.get(); } + bool allowPopups() const { return m_allowPopups; } + +private: + PluginRequest(uint32_t requestID, NSURLRequest* request, NSString* frameName, bool allowPopups) + : m_requestID(requestID) + , m_request(request) + , m_frameName(frameName) + , m_allowPopups(allowPopups) + { + } + + uint32_t m_requestID; + RetainPtr<NSURLRequest*> m_request; + RetainPtr<NSString*> m_frameName; + bool m_allowPopups; +}; + +NetscapePluginInstanceProxy::LocalObjectMap::LocalObjectMap() + : m_objectIDCounter(0) +{ +} + +NetscapePluginInstanceProxy::LocalObjectMap::~LocalObjectMap() +{ +} + +inline bool NetscapePluginInstanceProxy::LocalObjectMap::contains(uint32_t objectID) const +{ + return m_idToJSObjectMap.contains(objectID); +} + +inline JSC::JSObject* NetscapePluginInstanceProxy::LocalObjectMap::get(uint32_t objectID) const +{ + if (objectID == HashTraits<uint32_t>::emptyValue() || HashTraits<uint32_t>::isDeletedValue(objectID)) + return 0; + + return m_idToJSObjectMap.get(objectID).get(); +} + +uint32_t NetscapePluginInstanceProxy::LocalObjectMap::idForObject(JSGlobalData& globalData, JSObject* object) +{ + // This method creates objects with refcount of 1, but doesn't increase refcount when returning + // found objects. This extra count accounts for the main "reference" kept by plugin process. + + // To avoid excessive IPC, plugin process doesn't send each NPObject release/retain call to + // Safari. It only sends one when the last reference is removed, and it can destroy the proxy + // NPObject. + + // However, the browser may be sending the same object out to plug-in as a function call + // argument at the same time - neither side can know what the other one is doing. So, + // is to make PCForgetBrowserObject call return a boolean result, making it possible for + // the browser to make plugin host keep the proxy with zero refcount for a little longer. + + uint32_t objectID = 0; + + HashMap<JSC::JSObject*, pair<uint32_t, uint32_t> >::iterator iter = m_jsObjectToIDMap.find(object); + if (iter != m_jsObjectToIDMap.end()) + return iter->second.first; + + do { + objectID = ++m_objectIDCounter; + } while (!m_objectIDCounter || m_objectIDCounter == static_cast<uint32_t>(-1) || m_idToJSObjectMap.contains(objectID)); + + m_idToJSObjectMap.set(objectID, Strong<JSObject>(globalData, object)); + m_jsObjectToIDMap.set(object, make_pair<uint32_t, uint32_t>(objectID, 1)); + + return objectID; +} + +void NetscapePluginInstanceProxy::LocalObjectMap::retain(JSC::JSObject* object) +{ + HashMap<JSC::JSObject*, pair<uint32_t, uint32_t> >::iterator iter = m_jsObjectToIDMap.find(object); + ASSERT(iter != m_jsObjectToIDMap.end()); + + iter->second.second = iter->second.second + 1; +} + +void NetscapePluginInstanceProxy::LocalObjectMap::release(JSC::JSObject* object) +{ + HashMap<JSC::JSObject*, pair<uint32_t, uint32_t> >::iterator iter = m_jsObjectToIDMap.find(object); + ASSERT(iter != m_jsObjectToIDMap.end()); + + ASSERT(iter->second.second > 0); + iter->second.second = iter->second.second - 1; + if (!iter->second.second) { + m_idToJSObjectMap.remove(iter->second.first); + m_jsObjectToIDMap.remove(iter); + } +} + +void NetscapePluginInstanceProxy::LocalObjectMap::clear() +{ + m_idToJSObjectMap.clear(); + m_jsObjectToIDMap.clear(); +} + +bool NetscapePluginInstanceProxy::LocalObjectMap::forget(uint32_t objectID) +{ + if (objectID == HashTraits<uint32_t>::emptyValue() || HashTraits<uint32_t>::isDeletedValue(objectID)) { + LOG_ERROR("NetscapePluginInstanceProxy::LocalObjectMap::forget: local object id %u is not valid.", objectID); + return true; + } + + HashMap<uint32_t, JSC::Strong<JSC::JSObject> >::iterator iter = m_idToJSObjectMap.find(objectID); + if (iter == m_idToJSObjectMap.end()) { + LOG_ERROR("NetscapePluginInstanceProxy::LocalObjectMap::forget: local object %u doesn't exist.", objectID); + return true; + } + + HashMap<JSC::JSObject*, pair<uint32_t, uint32_t> >::iterator rIter = m_jsObjectToIDMap.find(iter->second.get()); + + // If the object is being sent to plug-in right now, then it's not the time to forget. + if (rIter->second.second != 1) + return false; + + m_jsObjectToIDMap.remove(rIter); + m_idToJSObjectMap.remove(iter); + return true; +} + +static uint32_t pluginIDCounter; + +bool NetscapePluginInstanceProxy::m_inDestroy; + +#ifndef NDEBUG +static WTF::RefCountedLeakCounter netscapePluginInstanceProxyCounter("NetscapePluginInstanceProxy"); +#endif + +NetscapePluginInstanceProxy::NetscapePluginInstanceProxy(NetscapePluginHostProxy* pluginHostProxy, WebHostedNetscapePluginView *pluginView, bool fullFramePlugin) + : m_pluginHostProxy(pluginHostProxy) + , m_pluginView(pluginView) + , m_requestTimer(this, &NetscapePluginInstanceProxy::requestTimerFired) + , m_currentURLRequestID(0) + , m_renderContextID(0) + , m_rendererType(UseSoftwareRenderer) + , m_waitingForReply(false) + , m_urlCheckCounter(0) + , m_pluginFunctionCallDepth(0) + , m_shouldStopSoon(false) + , m_currentRequestID(0) + , m_pluginIsWaitingForDraw(false) +{ + ASSERT(m_pluginView); + + if (fullFramePlugin) { + // For full frame plug-ins, the first requestID will always be the one for the already + // open stream. + ++m_currentURLRequestID; + } + + // Assign a plug-in ID. + do { + m_pluginID = ++pluginIDCounter; + } while (pluginHostProxy->pluginInstance(m_pluginID) || !m_pluginID); + +#ifndef NDEBUG + netscapePluginInstanceProxyCounter.increment(); +#endif +} + +PassRefPtr<NetscapePluginInstanceProxy> NetscapePluginInstanceProxy::create(NetscapePluginHostProxy* pluginHostProxy, WebHostedNetscapePluginView *pluginView, bool fullFramePlugin) +{ + RefPtr<NetscapePluginInstanceProxy> proxy = adoptRef(new NetscapePluginInstanceProxy(pluginHostProxy, pluginView, fullFramePlugin)); + pluginHostProxy->addPluginInstance(proxy.get()); + return proxy.release(); +} + +NetscapePluginInstanceProxy::~NetscapePluginInstanceProxy() +{ + ASSERT(!m_pluginHostProxy); + + m_pluginID = 0; + deleteAllValues(m_replies); + +#ifndef NDEBUG + netscapePluginInstanceProxyCounter.decrement(); +#endif +} + +void NetscapePluginInstanceProxy::resize(NSRect size, NSRect clipRect) +{ + uint32_t requestID = 0; + + requestID = nextRequestID(); + + _WKPHResizePluginInstance(m_pluginHostProxy->port(), m_pluginID, requestID, + size.origin.x, size.origin.y, size.size.width, size.size.height, + clipRect.origin.x, clipRect.origin.y, clipRect.size.width, clipRect.size.height); + + waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); +} + +void NetscapePluginInstanceProxy::stopAllStreams() +{ + Vector<RefPtr<HostedNetscapePluginStream> > streamsCopy; + copyValuesToVector(m_streams, streamsCopy); + for (size_t i = 0; i < streamsCopy.size(); i++) + streamsCopy[i]->stop(); +} + +void NetscapePluginInstanceProxy::cleanup() +{ + stopAllStreams(); + + m_requestTimer.stop(); + + // Clear the object map, this will cause any outstanding JS objects that the plug-in had a reference to + // to go away when the next garbage collection takes place. + m_localObjects.clear(); + + if (Frame* frame = core([m_pluginView webFrame])) + frame->script()->cleanupScriptObjectsForPlugin(m_pluginView); + + ProxyInstanceSet instances; + instances.swap(m_instances); + + // Invalidate all proxy instances. + ProxyInstanceSet::const_iterator end = instances.end(); + for (ProxyInstanceSet::const_iterator it = instances.begin(); it != end; ++it) + (*it)->invalidate(); + + m_pluginView = nil; + m_manualStream = 0; +} + +void NetscapePluginInstanceProxy::invalidate() +{ + // If the plug-in host has died, the proxy will be null. + if (!m_pluginHostProxy) + return; + + m_pluginHostProxy->removePluginInstance(this); + m_pluginHostProxy = 0; +} + +void NetscapePluginInstanceProxy::destroy() +{ + uint32_t requestID = nextRequestID(); + + ASSERT(!m_inDestroy); + m_inDestroy = true; + + FrameLoadMap::iterator end = m_pendingFrameLoads.end(); + for (FrameLoadMap::iterator it = m_pendingFrameLoads.begin(); it != end; ++it) + [(it->first) _setInternalLoadDelegate:nil]; + + _WKPHDestroyPluginInstance(m_pluginHostProxy->port(), m_pluginID, requestID); + + // If the plug-in host crashes while we're waiting for a reply, the last reference to the instance proxy + // will go away. Prevent this by protecting it here. + RefPtr<NetscapePluginInstanceProxy> protect(this); + + // We don't care about the reply here - we just want to block until the plug-in instance has been torn down. + waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); + + m_inDestroy = false; + + cleanup(); + invalidate(); +} + +void NetscapePluginInstanceProxy::setManualStream(PassRefPtr<HostedNetscapePluginStream> manualStream) +{ + ASSERT(!m_manualStream); + + m_manualStream = manualStream; +} + +bool NetscapePluginInstanceProxy::cancelStreamLoad(uint32_t streamID, NPReason reason) +{ + HostedNetscapePluginStream* stream = 0; + + if (m_manualStream && streamID == 1) + stream = m_manualStream.get(); + else + stream = m_streams.get(streamID).get(); + + if (!stream) + return false; + + stream->cancelLoad(reason); + return true; +} + +void NetscapePluginInstanceProxy::disconnectStream(HostedNetscapePluginStream* stream) +{ + if (stream == m_manualStream) { + m_manualStream = 0; + return; + } + + ASSERT(m_streams.get(stream->streamID()) == stream); + m_streams.remove(stream->streamID()); +} + +void NetscapePluginInstanceProxy::pluginHostDied() +{ + m_pluginHostProxy = 0; + + [m_pluginView pluginHostDied]; + + cleanup(); +} + +void NetscapePluginInstanceProxy::focusChanged(bool hasFocus) +{ + _WKPHPluginInstanceFocusChanged(m_pluginHostProxy->port(), m_pluginID, hasFocus); +} + +void NetscapePluginInstanceProxy::windowFocusChanged(bool hasFocus) +{ + _WKPHPluginInstanceWindowFocusChanged(m_pluginHostProxy->port(), m_pluginID, hasFocus); +} + +void NetscapePluginInstanceProxy::windowFrameChanged(NSRect frame) +{ + _WKPHPluginInstanceWindowFrameChanged(m_pluginHostProxy->port(), m_pluginID, frame.origin.x, frame.origin.y, frame.size.width, frame.size.height, + NSMaxY([[[NSScreen screens] objectAtIndex:0] frame])); +} + +void NetscapePluginInstanceProxy::startTimers(bool throttleTimers) +{ + _WKPHPluginInstanceStartTimers(m_pluginHostProxy->port(), m_pluginID, throttleTimers); +} + +void NetscapePluginInstanceProxy::mouseEvent(NSView *pluginView, NSEvent *event, NPCocoaEventType type) +{ + NSPoint screenPoint = [[event window] convertBaseToScreen:[event locationInWindow]]; + NSPoint pluginPoint = [pluginView convertPoint:[event locationInWindow] fromView:nil]; + + int clickCount; + if (type == NPCocoaEventMouseEntered || type == NPCocoaEventMouseExited) + clickCount = 0; + else + clickCount = [event clickCount]; + + + _WKPHPluginInstanceMouseEvent(m_pluginHostProxy->port(), m_pluginID, + [event timestamp], + type, [event modifierFlags], + pluginPoint.x, pluginPoint.y, + screenPoint.x, screenPoint.y, + NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]), + [event buttonNumber], clickCount, + [event deltaX], [event deltaY], [event deltaZ]); +} + +void NetscapePluginInstanceProxy::keyEvent(NSView *pluginView, NSEvent *event, NPCocoaEventType type) +{ + NSData *charactersData = [[event characters] dataUsingEncoding:NSUTF8StringEncoding]; + NSData *charactersIgnoringModifiersData = [[event charactersIgnoringModifiers] dataUsingEncoding:NSUTF8StringEncoding]; + + _WKPHPluginInstanceKeyboardEvent(m_pluginHostProxy->port(), m_pluginID, + [event timestamp], + type, [event modifierFlags], + const_cast<char*>(reinterpret_cast<const char*>([charactersData bytes])), [charactersData length], + const_cast<char*>(reinterpret_cast<const char*>([charactersIgnoringModifiersData bytes])), [charactersIgnoringModifiersData length], + [event isARepeat], [event keyCode], WKGetNSEventKeyChar(event)); +} + +void NetscapePluginInstanceProxy::syntheticKeyDownWithCommandModifier(int keyCode, char character) +{ + NSData *charactersData = [NSData dataWithBytes:&character length:1]; + + _WKPHPluginInstanceKeyboardEvent(m_pluginHostProxy->port(), m_pluginID, + [NSDate timeIntervalSinceReferenceDate], + NPCocoaEventKeyDown, NSCommandKeyMask, + const_cast<char*>(reinterpret_cast<const char*>([charactersData bytes])), [charactersData length], + const_cast<char*>(reinterpret_cast<const char*>([charactersData bytes])), [charactersData length], + false, keyCode, character); +} + +void NetscapePluginInstanceProxy::flagsChanged(NSEvent *event) +{ + _WKPHPluginInstanceKeyboardEvent(m_pluginHostProxy->port(), m_pluginID, + [event timestamp], NPCocoaEventFlagsChanged, + [event modifierFlags], 0, 0, 0, 0, false, [event keyCode], 0); +} + +void NetscapePluginInstanceProxy::insertText(NSString *text) +{ + NSData *textData = [text dataUsingEncoding:NSUTF8StringEncoding]; + + _WKPHPluginInstanceInsertText(m_pluginHostProxy->port(), m_pluginID, + const_cast<char*>(reinterpret_cast<const char*>([textData bytes])), [textData length]); +} + +bool NetscapePluginInstanceProxy::wheelEvent(NSView *pluginView, NSEvent *event) +{ + NSPoint pluginPoint = [pluginView convertPoint:[event locationInWindow] fromView:nil]; + + uint32_t requestID = nextRequestID(); + _WKPHPluginInstanceWheelEvent(m_pluginHostProxy->port(), m_pluginID, requestID, + [event timestamp], [event modifierFlags], + pluginPoint.x, pluginPoint.y, [event buttonNumber], + [event deltaX], [event deltaY], [event deltaZ]); + + auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); + if (!reply.get() || !reply->m_result) + return false; + + return true; +} + +void NetscapePluginInstanceProxy::print(CGContextRef context, unsigned width, unsigned height) +{ + uint32_t requestID = nextRequestID(); + _WKPHPluginInstancePrint(m_pluginHostProxy->port(), m_pluginID, requestID, width, height); + + auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID); + if (!reply.get() || !reply->m_returnValue) + return; + + RetainPtr<CGDataProvider> dataProvider(AdoptCF, CGDataProviderCreateWithCFData(reply->m_result.get())); + RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB()); + RetainPtr<CGImageRef> image(AdoptCF, CGImageCreate(width, height, 8, 32, width * 4, colorSpace.get(), kCGImageAlphaFirst, dataProvider.get(), 0, false, kCGRenderingIntentDefault)); + + // Flip the context and draw the image. + CGContextSaveGState(context); + CGContextTranslateCTM(context, 0.0, height); + CGContextScaleCTM(context, 1.0, -1.0); + + CGContextDrawImage(context, CGRectMake(0, 0, width, height), image.get()); + + CGContextRestoreGState(context); +} + +void NetscapePluginInstanceProxy::snapshot(CGContextRef context, unsigned width, unsigned height) +{ + uint32_t requestID = nextRequestID(); + _WKPHPluginInstanceSnapshot(m_pluginHostProxy->port(), m_pluginID, requestID, width, height); + + auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID); + if (!reply.get() || !reply->m_returnValue) + return; + + RetainPtr<CGDataProvider> dataProvider(AdoptCF, CGDataProviderCreateWithCFData(reply->m_result.get())); + RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB()); + RetainPtr<CGImageRef> image(AdoptCF, CGImageCreate(width, height, 8, 32, width * 4, colorSpace.get(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, dataProvider.get(), 0, false, kCGRenderingIntentDefault)); + + CGContextDrawImage(context, CGRectMake(0, 0, width, height), image.get()); +} + +void NetscapePluginInstanceProxy::stopTimers() +{ + _WKPHPluginInstanceStopTimers(m_pluginHostProxy->port(), m_pluginID); +} + +void NetscapePluginInstanceProxy::status(const char* message) +{ + RetainPtr<CFStringRef> status(AdoptCF, CFStringCreateWithCString(0, message ? message : "", kCFStringEncodingUTF8)); + if (!status) + return; + + WebView *wv = [m_pluginView webView]; + [[wv _UIDelegateForwarder] webView:wv setStatusText:(NSString *)status.get()]; +} + +NPError NetscapePluginInstanceProxy::loadURL(const char* url, const char* target, const char* postData, uint32_t postLen, LoadURLFlags flags, uint32_t& streamID) +{ + if (!url) + return NPERR_INVALID_PARAM; + + NSMutableURLRequest *request = [m_pluginView requestWithURLCString:url]; + + if (flags & IsPost) { + NSData *httpBody = nil; + + if (flags & PostDataIsFile) { + // If we're posting a file, buf is either a file URL or a path to the file. + if (!postData) + return NPERR_INVALID_PARAM; + RetainPtr<CFStringRef> bufString(AdoptCF, CFStringCreateWithCString(kCFAllocatorDefault, postData, kCFStringEncodingWindowsLatin1)); + if (!bufString) + return NPERR_INVALID_PARAM; + + NSURL *fileURL = [NSURL _web_URLWithDataAsString:(NSString *)bufString.get()]; + NSString *path; + if ([fileURL isFileURL]) + path = [fileURL path]; + else + path = (NSString *)bufString.get(); + httpBody = [NSData dataWithContentsOfFile:[path _webkit_fixedCarbonPOSIXPath]]; + if (!httpBody) + return NPERR_FILE_NOT_FOUND; + } else + httpBody = [NSData dataWithBytes:postData length:postLen]; + + if (![httpBody length]) + return NPERR_INVALID_PARAM; + + [request setHTTPMethod:@"POST"]; + + if (flags & AllowHeadersInPostData) { + if ([httpBody _web_startsWithBlankLine]) + httpBody = [httpBody subdataWithRange:NSMakeRange(1, [httpBody length] - 1)]; + else { + NSInteger location = [httpBody _web_locationAfterFirstBlankLine]; + if (location != NSNotFound) { + // If the blank line is somewhere in the middle of postData, everything before is the header. + NSData *headerData = [httpBody subdataWithRange:NSMakeRange(0, location)]; + NSMutableDictionary *header = [headerData _webkit_parseRFC822HeaderFields]; + unsigned dataLength = [httpBody length] - location; + + // Sometimes plugins like to set Content-Length themselves when they post, + // but CFNetwork does not like that. So we will remove the header + // and instead truncate the data to the requested length. + NSString *contentLength = [header objectForKey:@"Content-Length"]; + + if (contentLength) + dataLength = min(static_cast<unsigned>([contentLength intValue]), dataLength); + [header removeObjectForKey:@"Content-Length"]; + + if ([header count] > 0) + [request setAllHTTPHeaderFields:header]; + + // Everything after the blank line is the actual content of the POST. + httpBody = [httpBody subdataWithRange:NSMakeRange(location, dataLength)]; + } + } + } + + if (![httpBody length]) + return NPERR_INVALID_PARAM; + + // Plug-ins expect to receive uncached data when doing a POST (3347134). + [request setCachePolicy:NSURLRequestReloadIgnoringCacheData]; + [request setHTTPBody:httpBody]; + } + + return loadRequest(request, target, flags & AllowPopups, streamID); +} + +void NetscapePluginInstanceProxy::performRequest(PluginRequest* pluginRequest) +{ + ASSERT(m_pluginView); + + NSURLRequest *request = pluginRequest->request(); + NSString *frameName = pluginRequest->frameName(); + WebFrame *frame = nil; + + NSURL *URL = [request URL]; + NSString *JSString = [URL _webkit_scriptIfJavaScriptURL]; + + ASSERT(frameName || JSString); + if (frameName) { + // FIXME - need to get rid of this window creation which + // bypasses normal targeted link handling + frame = kit(core([m_pluginView webFrame])->loader()->findFrameForNavigation(frameName)); + if (!frame) { + WebView *currentWebView = [m_pluginView webView]; + NSDictionary *features = [[NSDictionary alloc] init]; + WebView *newWebView = [[currentWebView _UIDelegateForwarder] webView:currentWebView + createWebViewWithRequest:nil + windowFeatures:features]; + [features release]; + + if (!newWebView) { + _WKPHLoadURLNotify(m_pluginHostProxy->port(), m_pluginID, pluginRequest->requestID(), NPERR_GENERIC_ERROR); + return; + } + + frame = [newWebView mainFrame]; + core(frame)->tree()->setName(frameName); + [[newWebView _UIDelegateForwarder] webViewShow:newWebView]; + } + } + + if (JSString) { + ASSERT(!frame || [m_pluginView webFrame] == frame); + evaluateJavaScript(pluginRequest); + } else { + [frame loadRequest:request]; + + // Check if another plug-in view or even this view is waiting for the frame to load. + // If it is, tell it that the load was cancelled because it will be anyway. + WebHostedNetscapePluginView *view = [frame _internalLoadDelegate]; + if (view != nil) { + ASSERT([view isKindOfClass:[WebHostedNetscapePluginView class]]); + [view webFrame:frame didFinishLoadWithReason:NPRES_USER_BREAK]; + } + m_pendingFrameLoads.set(frame, pluginRequest); + [frame _setInternalLoadDelegate:m_pluginView]; + } + +} + +void NetscapePluginInstanceProxy::webFrameDidFinishLoadWithReason(WebFrame* webFrame, NPReason reason) +{ + FrameLoadMap::iterator it = m_pendingFrameLoads.find(webFrame); + ASSERT(it != m_pendingFrameLoads.end()); + + PluginRequest* pluginRequest = it->second.get(); + _WKPHLoadURLNotify(m_pluginHostProxy->port(), m_pluginID, pluginRequest->requestID(), reason); + + m_pendingFrameLoads.remove(it); + + [webFrame _setInternalLoadDelegate:nil]; +} + +void NetscapePluginInstanceProxy::evaluateJavaScript(PluginRequest* pluginRequest) +{ + NSURL *URL = [pluginRequest->request() URL]; + NSString *JSString = [URL _webkit_scriptIfJavaScriptURL]; + ASSERT(JSString); + + NSString *result = [[m_pluginView webFrame] _stringByEvaluatingJavaScriptFromString:JSString forceUserGesture:pluginRequest->allowPopups()]; + + // Don't continue if stringByEvaluatingJavaScriptFromString caused the plug-in to stop. + if (!m_pluginHostProxy) + return; + + if (pluginRequest->frameName() != nil) + return; + + if ([result length] > 0) { + // Don't call NPP_NewStream and other stream methods if there is no JS result to deliver. This is what Mozilla does. + NSData *JSData = [result dataUsingEncoding:NSUTF8StringEncoding]; + + RefPtr<HostedNetscapePluginStream> stream = HostedNetscapePluginStream::create(this, pluginRequest->requestID(), pluginRequest->request()); + m_streams.add(stream->streamID(), stream); + + RetainPtr<NSURLResponse> response(AdoptNS, [[NSURLResponse alloc] initWithURL:URL + MIMEType:@"text/plain" + expectedContentLength:[JSData length] + textEncodingName:nil]); + stream->startStreamWithResponse(response.get()); + stream->didReceiveData(0, static_cast<const char*>([JSData bytes]), [JSData length]); + stream->didFinishLoading(0); + } +} + +void NetscapePluginInstanceProxy::requestTimerFired(Timer<NetscapePluginInstanceProxy>*) +{ + ASSERT(!m_pluginRequests.isEmpty()); + ASSERT(m_pluginView); + + RefPtr<PluginRequest> request = m_pluginRequests.first(); + m_pluginRequests.removeFirst(); + + if (!m_pluginRequests.isEmpty()) + m_requestTimer.startOneShot(0); + + performRequest(request.get()); +} + +NPError NetscapePluginInstanceProxy::loadRequest(NSURLRequest *request, const char* cTarget, bool allowPopups, uint32_t& requestID) +{ + NSURL *URL = [request URL]; + + if (!URL) + return NPERR_INVALID_URL; + + // Don't allow requests to be loaded when the document loader is stopping all loaders. + DocumentLoader* documentLoader = [[m_pluginView dataSource] _documentLoader]; + if (!documentLoader || documentLoader->isStopping()) + return NPERR_GENERIC_ERROR; + + NSString *target = nil; + if (cTarget) { + // Find the frame given the target string. + target = [NSString stringWithCString:cTarget encoding:NSISOLatin1StringEncoding]; + } + WebFrame *frame = [m_pluginView webFrame]; + + // don't let a plugin start any loads if it is no longer part of a document that is being + // displayed unless the loads are in the same frame as the plugin. + if (documentLoader != core([m_pluginView webFrame])->loader()->activeDocumentLoader() && + (!cTarget || [frame findFrameNamed:target] != frame)) { + return NPERR_GENERIC_ERROR; + } + + NSString *JSString = [URL _webkit_scriptIfJavaScriptURL]; + if (JSString != nil) { + if (![[[m_pluginView webView] preferences] isJavaScriptEnabled]) { + // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does. + return NPERR_GENERIC_ERROR; + } + } else { + if (!core([m_pluginView webFrame])->document()->securityOrigin()->canDisplay(URL)) + return NPERR_GENERIC_ERROR; + } + + // FIXME: Handle wraparound + requestID = ++m_currentURLRequestID; + + if (cTarget || JSString) { + // Make when targetting a frame or evaluating a JS string, perform the request after a delay because we don't + // want to potentially kill the plug-in inside of its URL request. + + if (JSString && target && [frame findFrameNamed:target] != frame) { + // For security reasons, only allow JS requests to be made on the frame that contains the plug-in. + return NPERR_INVALID_PARAM; + } + + RefPtr<PluginRequest> pluginRequest = PluginRequest::create(requestID, request, target, allowPopups); + m_pluginRequests.append(pluginRequest.release()); + m_requestTimer.startOneShot(0); + } else { + RefPtr<HostedNetscapePluginStream> stream = HostedNetscapePluginStream::create(this, requestID, request); + + ASSERT(!m_streams.contains(requestID)); + m_streams.add(requestID, stream); + stream->start(); + } + + return NPERR_NO_ERROR; +} + +NetscapePluginInstanceProxy::Reply* NetscapePluginInstanceProxy::processRequestsAndWaitForReply(uint32_t requestID) +{ + Reply* reply = 0; + + ASSERT(m_pluginHostProxy); + while (!(reply = m_replies.take(requestID))) { + if (!m_pluginHostProxy->processRequests()) + return 0; + + // The host proxy can be destroyed while executing a nested processRequests() call, in which case it's normal + // to get a success result, but be unable to keep looping. + if (!m_pluginHostProxy) + return 0; + } + + ASSERT(reply); + return reply; +} + +// NPRuntime support +bool NetscapePluginInstanceProxy::getWindowNPObject(uint32_t& objectID) +{ + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + if (!frame->script()->canExecuteScripts(NotAboutToExecuteScript)) + objectID = 0; + else + objectID = m_localObjects.idForObject(*pluginWorld()->globalData(), frame->script()->windowShell(pluginWorld())->window()); + + return true; +} + +bool NetscapePluginInstanceProxy::getPluginElementNPObject(uint32_t& objectID) +{ + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + if (JSObject* object = frame->script()->jsObjectForPluginElement([m_pluginView element])) + objectID = m_localObjects.idForObject(*pluginWorld()->globalData(), object); + else + objectID = 0; + + return true; +} + +bool NetscapePluginInstanceProxy::forgetBrowserObjectID(uint32_t objectID) +{ + return m_localObjects.forget(objectID); +} + +bool NetscapePluginInstanceProxy::evaluate(uint32_t objectID, const String& script, data_t& resultData, mach_msg_type_number_t& resultLength, bool allowPopups) +{ + resultData = 0; + resultLength = 0; + + if (m_inDestroy) + return false; + + if (!m_localObjects.contains(objectID)) { + LOG_ERROR("NetscapePluginInstanceProxy::evaluate: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + JSLock lock(SilenceAssertionsOnly); + + Strong<JSGlobalObject> globalObject(*pluginWorld()->globalData(), frame->script()->globalObject(pluginWorld())); + ExecState* exec = globalObject->globalExec(); + + bool oldAllowPopups = frame->script()->allowPopupsFromPlugin(); + frame->script()->setAllowPopupsFromPlugin(allowPopups); + + globalObject->globalData().timeoutChecker.start(); + Completion completion = JSC::evaluate(exec, globalObject->globalScopeChain(), makeSource(script)); + globalObject->globalData().timeoutChecker.stop(); + ComplType type = completion.complType(); + + frame->script()->setAllowPopupsFromPlugin(oldAllowPopups); + + JSValue result; + if (type == Normal) + result = completion.value(); + + if (!result) + result = jsUndefined(); + + marshalValue(exec, result, resultData, resultLength); + exec->clearException(); + return true; +} + +bool NetscapePluginInstanceProxy::invoke(uint32_t objectID, const Identifier& methodName, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength) +{ + resultData = 0; + resultLength = 0; + + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::invoke: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + JSLock lock(SilenceAssertionsOnly); + JSValue function = object->get(exec, methodName); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return false; + + MarkedArgumentBuffer argList; + demarshalValues(exec, argumentsData, argumentsLength, argList); + + RefPtr<JSGlobalData> globalData = pluginWorld()->globalData(); + globalData->timeoutChecker.start(); + JSValue value = call(exec, function, callType, callData, object, argList); + globalData->timeoutChecker.stop(); + + marshalValue(exec, value, resultData, resultLength); + exec->clearException(); + return true; +} + +bool NetscapePluginInstanceProxy::invokeDefault(uint32_t objectID, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::invokeDefault: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + JSLock lock(SilenceAssertionsOnly); + CallData callData; + CallType callType = object->getCallData(callData); + if (callType == CallTypeNone) + return false; + + MarkedArgumentBuffer argList; + demarshalValues(exec, argumentsData, argumentsLength, argList); + + RefPtr<JSGlobalData> globalData = pluginWorld()->globalData(); + globalData->timeoutChecker.start(); + JSValue value = call(exec, object, callType, callData, object, argList); + globalData->timeoutChecker.stop(); + + marshalValue(exec, value, resultData, resultLength); + exec->clearException(); + return true; +} + +bool NetscapePluginInstanceProxy::construct(uint32_t objectID, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::construct: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + JSLock lock(SilenceAssertionsOnly); + + ConstructData constructData; + ConstructType constructType = object->getConstructData(constructData); + if (constructType == ConstructTypeNone) + return false; + + MarkedArgumentBuffer argList; + demarshalValues(exec, argumentsData, argumentsLength, argList); + + RefPtr<JSGlobalData> globalData = pluginWorld()->globalData(); + globalData->timeoutChecker.start(); + JSValue value = JSC::construct(exec, object, constructType, constructData, argList); + globalData->timeoutChecker.stop(); + + marshalValue(exec, value, resultData, resultLength); + exec->clearException(); + return true; +} + +bool NetscapePluginInstanceProxy::getProperty(uint32_t objectID, const Identifier& propertyName, data_t& resultData, mach_msg_type_number_t& resultLength) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::getProperty: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + JSLock lock(SilenceAssertionsOnly); + JSValue value = object->get(exec, propertyName); + + marshalValue(exec, value, resultData, resultLength); + exec->clearException(); + return true; +} + +bool NetscapePluginInstanceProxy::getProperty(uint32_t objectID, unsigned propertyName, data_t& resultData, mach_msg_type_number_t& resultLength) +{ + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::getProperty: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + JSLock lock(SilenceAssertionsOnly); + JSValue value = object->get(exec, propertyName); + + marshalValue(exec, value, resultData, resultLength); + exec->clearException(); + return true; +} + +bool NetscapePluginInstanceProxy::setProperty(uint32_t objectID, const Identifier& propertyName, data_t valueData, mach_msg_type_number_t valueLength) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::setProperty: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + JSLock lock(SilenceAssertionsOnly); + + JSValue value = demarshalValue(exec, valueData, valueLength); + PutPropertySlot slot; + object->put(exec, propertyName, value, slot); + + exec->clearException(); + return true; +} + +bool NetscapePluginInstanceProxy::setProperty(uint32_t objectID, unsigned propertyName, data_t valueData, mach_msg_type_number_t valueLength) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::setProperty: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + JSLock lock(SilenceAssertionsOnly); + + JSValue value = demarshalValue(exec, valueData, valueLength); + object->put(exec, propertyName, value); + + exec->clearException(); + return true; +} + +bool NetscapePluginInstanceProxy::removeProperty(uint32_t objectID, const Identifier& propertyName) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::removeProperty: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + if (!object->hasProperty(exec, propertyName)) { + exec->clearException(); + return false; + } + + JSLock lock(SilenceAssertionsOnly); + object->deleteProperty(exec, propertyName); + exec->clearException(); + return true; +} + +bool NetscapePluginInstanceProxy::removeProperty(uint32_t objectID, unsigned propertyName) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::removeProperty: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + if (!object->hasProperty(exec, propertyName)) { + exec->clearException(); + return false; + } + + JSLock lock(SilenceAssertionsOnly); + object->deleteProperty(exec, propertyName); + exec->clearException(); + return true; +} + +bool NetscapePluginInstanceProxy::hasProperty(uint32_t objectID, const Identifier& propertyName) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::hasProperty: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + bool result = object->hasProperty(exec, propertyName); + exec->clearException(); + + return result; +} + +bool NetscapePluginInstanceProxy::hasProperty(uint32_t objectID, unsigned propertyName) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::hasProperty: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + bool result = object->hasProperty(exec, propertyName); + exec->clearException(); + + return result; +} + +bool NetscapePluginInstanceProxy::hasMethod(uint32_t objectID, const Identifier& methodName) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::hasMethod: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + JSLock lock(SilenceAssertionsOnly); + JSValue func = object->get(exec, methodName); + exec->clearException(); + return !func.isUndefined(); +} + +bool NetscapePluginInstanceProxy::enumerate(uint32_t objectID, data_t& resultData, mach_msg_type_number_t& resultLength) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::enumerate: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + JSLock lock(SilenceAssertionsOnly); + + PropertyNameArray propertyNames(exec); + object->getPropertyNames(exec, propertyNames); + + RetainPtr<NSMutableArray*> array(AdoptNS, [[NSMutableArray alloc] init]); + for (unsigned i = 0; i < propertyNames.size(); i++) { + uint64_t methodName = reinterpret_cast<uint64_t>(_NPN_GetStringIdentifier(propertyNames[i].ustring().utf8().data())); + + [array.get() addObject:[NSNumber numberWithLongLong:methodName]]; + } + + NSData *data = [NSPropertyListSerialization dataFromPropertyList:array.get() format:NSPropertyListBinaryFormat_v1_0 errorDescription:0]; + ASSERT(data); + + resultLength = [data length]; + mig_allocate(reinterpret_cast<vm_address_t*>(&resultData), resultLength); + + memcpy(resultData, [data bytes], resultLength); + + exec->clearException(); + + return true; +} + +void NetscapePluginInstanceProxy::addValueToArray(NSMutableArray *array, ExecState* exec, JSValue value) +{ + JSLock lock(SilenceAssertionsOnly); + + if (value.isString()) { + [array addObject:[NSNumber numberWithInt:StringValueType]]; + [array addObject:ustringToString(value.toString(exec))]; + } else if (value.isNumber()) { + [array addObject:[NSNumber numberWithInt:DoubleValueType]]; + [array addObject:[NSNumber numberWithDouble:value.toNumber(exec)]]; + } else if (value.isBoolean()) { + [array addObject:[NSNumber numberWithInt:BoolValueType]]; + [array addObject:[NSNumber numberWithBool:value.toBoolean(exec)]]; + } else if (value.isNull()) + [array addObject:[NSNumber numberWithInt:NullValueType]]; + else if (value.isObject()) { + JSObject* object = asObject(value); + if (object->classInfo() == &ProxyRuntimeObject::s_info) { + ProxyRuntimeObject* runtimeObject = static_cast<ProxyRuntimeObject*>(object); + if (ProxyInstance* instance = runtimeObject->getInternalProxyInstance()) { + [array addObject:[NSNumber numberWithInt:NPObjectValueType]]; + [array addObject:[NSNumber numberWithInt:instance->objectID()]]; + } + } else { + [array addObject:[NSNumber numberWithInt:JSObjectValueType]]; + [array addObject:[NSNumber numberWithInt:m_localObjects.idForObject(exec->globalData(), object)]]; + } + } else + [array addObject:[NSNumber numberWithInt:VoidValueType]]; +} + +void NetscapePluginInstanceProxy::marshalValue(ExecState* exec, JSValue value, data_t& resultData, mach_msg_type_number_t& resultLength) +{ + RetainPtr<NSMutableArray*> array(AdoptNS, [[NSMutableArray alloc] init]); + + addValueToArray(array.get(), exec, value); + + RetainPtr<NSData *> data = [NSPropertyListSerialization dataFromPropertyList:array.get() format:NSPropertyListBinaryFormat_v1_0 errorDescription:0]; + ASSERT(data); + + resultLength = [data.get() length]; + mig_allocate(reinterpret_cast<vm_address_t*>(&resultData), resultLength); + + memcpy(resultData, [data.get() bytes], resultLength); +} + +RetainPtr<NSData *> NetscapePluginInstanceProxy::marshalValues(ExecState* exec, const ArgList& args) +{ + RetainPtr<NSMutableArray*> array(AdoptNS, [[NSMutableArray alloc] init]); + + for (unsigned i = 0; i < args.size(); i++) + addValueToArray(array.get(), exec, args.at(i)); + + RetainPtr<NSData *> data = [NSPropertyListSerialization dataFromPropertyList:array.get() format:NSPropertyListBinaryFormat_v1_0 errorDescription:0]; + ASSERT(data); + + return data; +} + +bool NetscapePluginInstanceProxy::demarshalValueFromArray(ExecState* exec, NSArray *array, NSUInteger& index, JSValue& result) +{ + if (index == [array count]) + return false; + + int type = [[array objectAtIndex:index++] intValue]; + switch (type) { + case VoidValueType: + result = jsUndefined(); + return true; + case NullValueType: + result = jsNull(); + return true; + case BoolValueType: + result = jsBoolean([[array objectAtIndex:index++] boolValue]); + return true; + case DoubleValueType: + result = jsNumber([[array objectAtIndex:index++] doubleValue]); + return true; + case StringValueType: { + NSString *string = [array objectAtIndex:index++]; + + result = jsString(exec, String(string)); + return true; + } + case JSObjectValueType: { + uint32_t objectID = [[array objectAtIndex:index++] intValue]; + + result = m_localObjects.get(objectID); + ASSERT(result); + return true; + } + case NPObjectValueType: { + uint32_t objectID = [[array objectAtIndex:index++] intValue]; + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + if (!frame->script()->canExecuteScripts(NotAboutToExecuteScript)) + return false; + + RefPtr<RootObject> rootObject = frame->script()->createRootObject(m_pluginView); + if (!rootObject) + return false; + + result = ProxyInstance::create(rootObject.release(), this, objectID)->createRuntimeObject(exec); + return true; + } + default: + ASSERT_NOT_REACHED(); + return false; + } +} + +JSValue NetscapePluginInstanceProxy::demarshalValue(ExecState* exec, const char* valueData, mach_msg_type_number_t valueLength) +{ + RetainPtr<NSData*> data(AdoptNS, [[NSData alloc] initWithBytesNoCopy:(void*)valueData length:valueLength freeWhenDone:NO]); + + RetainPtr<NSArray*> array = [NSPropertyListSerialization propertyListFromData:data.get() + mutabilityOption:NSPropertyListImmutable + format:0 + errorDescription:0]; + NSUInteger position = 0; + JSValue value; + bool result = demarshalValueFromArray(exec, array.get(), position, value); + ASSERT_UNUSED(result, result); + + return value; +} + +void NetscapePluginInstanceProxy::demarshalValues(ExecState* exec, data_t valuesData, mach_msg_type_number_t valuesLength, MarkedArgumentBuffer& result) +{ + RetainPtr<NSData*> data(AdoptNS, [[NSData alloc] initWithBytesNoCopy:valuesData length:valuesLength freeWhenDone:NO]); + + RetainPtr<NSArray*> array = [NSPropertyListSerialization propertyListFromData:data.get() + mutabilityOption:NSPropertyListImmutable + format:0 + errorDescription:0]; + NSUInteger position = 0; + JSValue value; + while (demarshalValueFromArray(exec, array.get(), position, value)) + result.append(value); +} + +void NetscapePluginInstanceProxy::retainLocalObject(JSC::JSValue value) +{ + if (!value.isObject() || value.inherits(&ProxyRuntimeObject::s_info)) + return; + + m_localObjects.retain(asObject(value)); +} + +void NetscapePluginInstanceProxy::releaseLocalObject(JSC::JSValue value) +{ + if (!value.isObject() || value.inherits(&ProxyRuntimeObject::s_info)) + return; + + m_localObjects.release(asObject(value)); +} + +PassRefPtr<Instance> NetscapePluginInstanceProxy::createBindingsInstance(PassRefPtr<RootObject> rootObject) +{ + uint32_t requestID = nextRequestID(); + + if (_WKPHGetScriptableNPObject(m_pluginHostProxy->port(), m_pluginID, requestID) != KERN_SUCCESS) + return 0; + + auto_ptr<GetScriptableNPObjectReply> reply = waitForReply<GetScriptableNPObjectReply>(requestID); + if (!reply.get()) + return 0; + + if (!reply->m_objectID) + return 0; + + // Since the reply was non-null, "this" is still a valid pointer. + return ProxyInstance::create(rootObject, this, reply->m_objectID); +} + +void NetscapePluginInstanceProxy::addInstance(ProxyInstance* instance) +{ + ASSERT(!m_instances.contains(instance)); + + m_instances.add(instance); +} + +void NetscapePluginInstanceProxy::removeInstance(ProxyInstance* instance) +{ + ASSERT(m_instances.contains(instance)); + + m_instances.remove(instance); +} + +void NetscapePluginInstanceProxy::willCallPluginFunction() +{ + m_pluginFunctionCallDepth++; +} + +void NetscapePluginInstanceProxy::didCallPluginFunction(bool& stopped) +{ + ASSERT(m_pluginFunctionCallDepth > 0); + m_pluginFunctionCallDepth--; + + // If -stop was called while we were calling into a plug-in function, and we're no longer + // inside a plug-in function, stop now. + if (!m_pluginFunctionCallDepth && m_shouldStopSoon) { + m_shouldStopSoon = false; + [m_pluginView stop]; + stopped = true; + } +} + +bool NetscapePluginInstanceProxy::shouldStop() +{ + if (m_pluginFunctionCallDepth) { + m_shouldStopSoon = true; + return false; + } + + return true; +} + +uint32_t NetscapePluginInstanceProxy::nextRequestID() +{ + uint32_t requestID = ++m_currentRequestID; + + // We don't want to return the HashMap empty/deleted "special keys" + if (requestID == 0 || requestID == static_cast<uint32_t>(-1)) + return nextRequestID(); + + return requestID; +} + +void NetscapePluginInstanceProxy::invalidateRect(double x, double y, double width, double height) +{ + ASSERT(m_pluginView); + + m_pluginIsWaitingForDraw = true; + [m_pluginView invalidatePluginContentRect:NSMakeRect(x, y, width, height)]; +} + +void NetscapePluginInstanceProxy::didDraw() +{ + if (!m_pluginIsWaitingForDraw) + return; + + m_pluginIsWaitingForDraw = false; + _WKPHPluginInstanceDidDraw(m_pluginHostProxy->port(), m_pluginID); +} + +bool NetscapePluginInstanceProxy::getCookies(data_t urlData, mach_msg_type_number_t urlLength, data_t& cookiesData, mach_msg_type_number_t& cookiesLength) +{ + ASSERT(m_pluginView); + + NSURL *url = [m_pluginView URLWithCString:urlData]; + if (!url) + return false; + + if (Frame* frame = core([m_pluginView webFrame])) { + String cookieString = cookies(frame->document(), url); + WTF::CString cookieStringUTF8 = cookieString.utf8(); + if (cookieStringUTF8.isNull()) + return false; + + cookiesLength = cookieStringUTF8.length(); + mig_allocate(reinterpret_cast<vm_address_t*>(&cookiesData), cookiesLength); + memcpy(cookiesData, cookieStringUTF8.data(), cookiesLength); + + return true; + } + + return false; +} + +bool NetscapePluginInstanceProxy::setCookies(data_t urlData, mach_msg_type_number_t urlLength, data_t cookiesData, mach_msg_type_number_t cookiesLength) +{ + ASSERT(m_pluginView); + + NSURL *url = [m_pluginView URLWithCString:urlData]; + if (!url) + return false; + + if (Frame* frame = core([m_pluginView webFrame])) { + String cookieString = String::fromUTF8(cookiesData, cookiesLength); + if (!cookieString) + return false; + + WebCore::setCookies(frame->document(), url, cookieString); + return true; + } + + return false; +} + +bool NetscapePluginInstanceProxy::getProxy(data_t urlData, mach_msg_type_number_t urlLength, data_t& proxyData, mach_msg_type_number_t& proxyLength) +{ + ASSERT(m_pluginView); + + NSURL *url = [m_pluginView URLWithCString:urlData]; + if (!url) + return false; + + Vector<ProxyServer> proxyServers = proxyServersForURL(url, 0); + WTF::CString proxyStringUTF8 = toString(proxyServers).utf8(); + + proxyLength = proxyStringUTF8.length(); + mig_allocate(reinterpret_cast<vm_address_t*>(&proxyData), proxyLength); + memcpy(proxyData, proxyStringUTF8.data(), proxyLength); + + return true; +} + +bool NetscapePluginInstanceProxy::getAuthenticationInfo(data_t protocolData, data_t hostData, uint32_t port, data_t schemeData, data_t realmData, + data_t& usernameData, mach_msg_type_number_t& usernameLength, data_t& passwordData, mach_msg_type_number_t& passwordLength) +{ + WTF::CString username; + WTF::CString password; + + if (!WebKit::getAuthenticationInfo(protocolData, hostData, port, schemeData, realmData, username, password)) + return false; + + usernameLength = username.length(); + mig_allocate(reinterpret_cast<vm_address_t*>(&usernameData), usernameLength); + memcpy(usernameData, username.data(), usernameLength); + + passwordLength = password.length(); + mig_allocate(reinterpret_cast<vm_address_t*>(&passwordData), passwordLength); + memcpy(passwordData, password.data(), passwordLength); + + return true; +} + +bool NetscapePluginInstanceProxy::convertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace, + double& destX, double& destY, NPCoordinateSpace destSpace) +{ + ASSERT(m_pluginView); + + return [m_pluginView convertFromX:sourceX andY:sourceY space:sourceSpace toX:&destX andY:&destY space:destSpace]; +} + +uint32_t NetscapePluginInstanceProxy::checkIfAllowedToLoadURL(const char* url, const char* target) +{ + uint32_t checkID; + + // Assign a check ID + do { + checkID = ++m_urlCheckCounter; + } while (m_urlChecks.contains(checkID) || !m_urlCheckCounter); + + NSString *frameName = target ? [NSString stringWithCString:target encoding:NSISOLatin1StringEncoding] : nil; + + NSNumber *contextInfo = [[NSNumber alloc] initWithUnsignedInt:checkID]; + WebPluginContainerCheck *check = [WebPluginContainerCheck checkWithRequest:[m_pluginView requestWithURLCString:url] + target:frameName + resultObject:m_pluginView + selector:@selector(_containerCheckResult:contextInfo:) + controller:m_pluginView + contextInfo:contextInfo]; + + [contextInfo release]; + m_urlChecks.set(checkID, check); + [check start]; + + return checkID; +} + +void NetscapePluginInstanceProxy::cancelCheckIfAllowedToLoadURL(uint32_t checkID) +{ + URLCheckMap::iterator it = m_urlChecks.find(checkID); + if (it == m_urlChecks.end()) + return; + + WebPluginContainerCheck *check = it->second.get(); + [check cancel]; + m_urlChecks.remove(it); +} + +void NetscapePluginInstanceProxy::checkIfAllowedToLoadURLResult(uint32_t checkID, bool allowed) +{ + _WKPHCheckIfAllowedToLoadURLResult(m_pluginHostProxy->port(), m_pluginID, checkID, allowed); +} + +void NetscapePluginInstanceProxy::resolveURL(const char* url, const char* target, data_t& resolvedURLData, mach_msg_type_number_t& resolvedURLLength) +{ + ASSERT(m_pluginView); + + WTF::CString resolvedURL = [m_pluginView resolvedURLStringForURL:url target:target]; + + resolvedURLLength = resolvedURL.length(); + mig_allocate(reinterpret_cast<vm_address_t*>(&resolvedURLData), resolvedURLLength); + memcpy(resolvedURLData, resolvedURL.data(), resolvedURLLength); +} + +void NetscapePluginInstanceProxy::privateBrowsingModeDidChange(bool isPrivateBrowsingEnabled) +{ + _WKPHPluginInstancePrivateBrowsingModeDidChange(m_pluginHostProxy->port(), m_pluginID, isPrivateBrowsingEnabled); +} + +static String& globalExceptionString() +{ + DEFINE_STATIC_LOCAL(String, exceptionString, ()); + return exceptionString; +} + +void NetscapePluginInstanceProxy::setGlobalException(const String& exception) +{ + globalExceptionString() = exception; +} + +void NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(ExecState* exec) +{ + if (globalExceptionString().isNull()) + return; + + { + JSLock lock(SilenceAssertionsOnly); + throwError(exec, createError(exec, stringToUString(globalExceptionString()))); + } + + globalExceptionString() = String(); +} + +} // namespace WebKit + +#endif // USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API) diff --git a/Source/WebKit/mac/Plugins/Hosted/ProxyInstance.h b/Source/WebKit/mac/Plugins/Hosted/ProxyInstance.h new file mode 100644 index 0000000..65f3a51 --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/ProxyInstance.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 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. + * + * 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. + */ + +#if USE(PLUGIN_HOST_PROCESS) + +#ifndef ProxyInstance_h +#define ProxyInstance_h + +#import "NetscapePluginInstanceProxy.h" +#import "WebKitPluginHostTypes.h" +#import <WebCore/BridgeJSC.h> +#import <WebCore/runtime_root.h> +#import <wtf/OwnPtr.h> + +namespace WebKit { + +class ProxyClass; + +class ProxyInstance : public JSC::Bindings::Instance { +public: + static PassRefPtr<ProxyInstance> create(PassRefPtr<JSC::Bindings::RootObject> rootObject, NetscapePluginInstanceProxy* instanceProxy, uint32_t objectID) + { + return adoptRef(new ProxyInstance(rootObject, instanceProxy, objectID)); + } + ~ProxyInstance(); + + JSC::Bindings::MethodList methodsNamed(const JSC::Identifier&); + JSC::Bindings::Field* fieldNamed(const JSC::Identifier&); + + JSC::JSValue fieldValue(JSC::ExecState*, const JSC::Bindings::Field*) const; + void setFieldValue(JSC::ExecState*, const JSC::Bindings::Field*, JSC::JSValue) const; + + void invalidate(); + + uint32_t objectID() const { return m_objectID; } + +private: + ProxyInstance(PassRefPtr<JSC::Bindings::RootObject>, NetscapePluginInstanceProxy*, uint32_t objectID); + + virtual JSC::Bindings::RuntimeObject* newRuntimeObject(JSC::ExecState*); + + virtual JSC::Bindings::Class* getClass() const; + + virtual JSC::JSValue getMethod(JSC::ExecState* exec, const JSC::Identifier& propertyName); + virtual JSC::JSValue invokeMethod(JSC::ExecState*, JSC::RuntimeMethod*); + + virtual bool supportsInvokeDefaultMethod() const; + virtual JSC::JSValue invokeDefaultMethod(JSC::ExecState*); + + virtual bool supportsConstruct() const; + virtual JSC::JSValue invokeConstruct(JSC::ExecState*, const JSC::ArgList&); + + virtual JSC::JSValue defaultValue(JSC::ExecState*, JSC::PreferredPrimitiveType) const; + virtual JSC::JSValue valueOf(JSC::ExecState*) const; + + virtual void getPropertyNames(JSC::ExecState*, JSC::PropertyNameArray&); + + JSC::JSValue stringValue(JSC::ExecState*) const; + JSC::JSValue numberValue(JSC::ExecState*) const; + JSC::JSValue booleanValue() const; + + JSC::JSValue invoke(JSC::ExecState*, InvokeType, uint64_t identifier, const JSC::ArgList&); + + template <typename T> + std::auto_ptr<T> waitForReply(uint32_t requestID) const { + std::auto_ptr<T> reply = m_instanceProxy->waitForReply<T>(requestID); + + // If the instance proxy was invalidated, just return a null reply. + if (!m_instanceProxy) + return std::auto_ptr<T>(); + + return reply; + } + + NetscapePluginInstanceProxy* m_instanceProxy; + uint32_t m_objectID; + JSC::Bindings::FieldMap m_fields; + JSC::Bindings::MethodMap m_methods; +}; + +} + +#endif // ProxyInstance_h +#endif // USE(PLUGIN_HOST_PROCESS) diff --git a/Source/WebKit/mac/Plugins/Hosted/ProxyInstance.mm b/Source/WebKit/mac/Plugins/Hosted/ProxyInstance.mm new file mode 100644 index 0000000..1f96c53 --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/ProxyInstance.mm @@ -0,0 +1,465 @@ +/* + * Copyright (C) 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. + * + * 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. + */ + +#if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API) + +#import "ProxyInstance.h" + +#import "NetscapePluginHostProxy.h" +#import "ProxyRuntimeObject.h" +#import <WebCore/IdentifierRep.h> +#import <WebCore/JSDOMWindow.h> +#import <WebCore/npruntime_impl.h> +#import <WebCore/runtime_method.h> +#import <runtime/Error.h> +#import <runtime/FunctionPrototype.h> +#import <runtime/PropertyNameArray.h> + +extern "C" { +#import "WebKitPluginHost.h" +} + +using namespace JSC; +using namespace JSC::Bindings; +using namespace std; +using namespace WebCore; + +namespace WebKit { + +class ProxyClass : public JSC::Bindings::Class { +private: + virtual MethodList methodsNamed(const Identifier&, Instance*) const; + virtual Field* fieldNamed(const Identifier&, Instance*) const; +}; + +MethodList ProxyClass::methodsNamed(const Identifier& identifier, Instance* instance) const +{ + return static_cast<ProxyInstance*>(instance)->methodsNamed(identifier); +} + +Field* ProxyClass::fieldNamed(const Identifier& identifier, Instance* instance) const +{ + return static_cast<ProxyInstance*>(instance)->fieldNamed(identifier); +} + +static ProxyClass* proxyClass() +{ + DEFINE_STATIC_LOCAL(ProxyClass, proxyClass, ()); + return &proxyClass; +} + +class ProxyField : public JSC::Bindings::Field { +public: + ProxyField(uint64_t serverIdentifier) + : m_serverIdentifier(serverIdentifier) + { + } + + uint64_t serverIdentifier() const { return m_serverIdentifier; } + +private: + virtual JSValue valueFromInstance(ExecState*, const Instance*) const; + virtual void setValueToInstance(ExecState*, const Instance*, JSValue) const; + + uint64_t m_serverIdentifier; +}; + +JSValue ProxyField::valueFromInstance(ExecState* exec, const Instance* instance) const +{ + return static_cast<const ProxyInstance*>(instance)->fieldValue(exec, this); +} + +void ProxyField::setValueToInstance(ExecState* exec, const Instance* instance, JSValue value) const +{ + static_cast<const ProxyInstance*>(instance)->setFieldValue(exec, this, value); +} + +class ProxyMethod : public JSC::Bindings::Method { +public: + ProxyMethod(uint64_t serverIdentifier) + : m_serverIdentifier(serverIdentifier) + { + } + + uint64_t serverIdentifier() const { return m_serverIdentifier; } + +private: + virtual int numParameters() const { return 0; } + + uint64_t m_serverIdentifier; +}; + +ProxyInstance::ProxyInstance(PassRefPtr<RootObject> rootObject, NetscapePluginInstanceProxy* instanceProxy, uint32_t objectID) + : Instance(rootObject) + , m_instanceProxy(instanceProxy) + , m_objectID(objectID) +{ + m_instanceProxy->addInstance(this); +} + +ProxyInstance::~ProxyInstance() +{ + deleteAllValues(m_fields); + deleteAllValues(m_methods); + + if (!m_instanceProxy) + return; + + m_instanceProxy->removeInstance(this); + + invalidate(); +} + +RuntimeObject* ProxyInstance::newRuntimeObject(ExecState* exec) +{ + return new (exec) ProxyRuntimeObject(exec, exec->lexicalGlobalObject(), this); +} + +JSC::Bindings::Class* ProxyInstance::getClass() const +{ + return proxyClass(); +} + +JSValue ProxyInstance::invoke(JSC::ExecState* exec, InvokeType type, uint64_t identifier, const ArgList& args) +{ + if (!m_instanceProxy) + return jsUndefined(); + + RetainPtr<NSData*> arguments(m_instanceProxy->marshalValues(exec, args)); + + uint32_t requestID = m_instanceProxy->nextRequestID(); + + for (unsigned i = 0; i < args.size(); i++) + m_instanceProxy->retainLocalObject(args.at(i)); + + if (_WKPHNPObjectInvoke(m_instanceProxy->hostProxy()->port(), m_instanceProxy->pluginID(), requestID, m_objectID, + type, identifier, (char*)[arguments.get() bytes], [arguments.get() length]) != KERN_SUCCESS) { + if (m_instanceProxy) { + for (unsigned i = 0; i < args.size(); i++) + m_instanceProxy->releaseLocalObject(args.at(i)); + } + return jsUndefined(); + } + + auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID); + NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec); + + if (m_instanceProxy) { + for (unsigned i = 0; i < args.size(); i++) + m_instanceProxy->releaseLocalObject(args.at(i)); + } + + if (!reply.get() || !reply->m_returnValue) + return jsUndefined(); + + return m_instanceProxy->demarshalValue(exec, (char*)CFDataGetBytePtr(reply->m_result.get()), CFDataGetLength(reply->m_result.get())); +} + +class ProxyRuntimeMethod : public RuntimeMethod { +public: + ProxyRuntimeMethod(ExecState* exec, JSGlobalObject* globalObject, const Identifier& name, Bindings::MethodList& list) + // FIXME: deprecatedGetDOMStructure uses the prototype off of the wrong global object + // exec-globalData() is also likely wrong. + : RuntimeMethod(exec, globalObject, deprecatedGetDOMStructure<ProxyRuntimeMethod>(exec), name, list) + { + ASSERT(inherits(&s_info)); + } + + static Structure* createStructure(JSGlobalData& globalData, JSValue prototype) + { + return Structure::create(globalData, prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount, &s_info); + } + + static const ClassInfo s_info; +}; + +const ClassInfo ProxyRuntimeMethod::s_info = { "ProxyRuntimeMethod", &RuntimeMethod::s_info, 0, 0 }; + +JSValue ProxyInstance::getMethod(JSC::ExecState* exec, const JSC::Identifier& propertyName) +{ + MethodList methodList = getClass()->methodsNamed(propertyName, this); + return new (exec) ProxyRuntimeMethod(exec, exec->lexicalGlobalObject(), propertyName, methodList); +} + +JSValue ProxyInstance::invokeMethod(ExecState* exec, JSC::RuntimeMethod* runtimeMethod) +{ + if (!asObject(runtimeMethod)->inherits(&ProxyRuntimeMethod::s_info)) + return throwError(exec, createTypeError(exec, "Attempt to invoke non-plug-in method on plug-in object.")); + + const MethodList& methodList = *runtimeMethod->methods(); + + ASSERT(methodList.size() == 1); + + ProxyMethod* method = static_cast<ProxyMethod*>(methodList[0]); + + return invoke(exec, Invoke, method->serverIdentifier(), ArgList(exec)); +} + +bool ProxyInstance::supportsInvokeDefaultMethod() const +{ + if (!m_instanceProxy) + return false; + + uint32_t requestID = m_instanceProxy->nextRequestID(); + + if (_WKPHNPObjectHasInvokeDefaultMethod(m_instanceProxy->hostProxy()->port(), + m_instanceProxy->pluginID(), requestID, + m_objectID) != KERN_SUCCESS) + return false; + + auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); + if (reply.get() && reply->m_result) + return true; + + return false; +} + +JSValue ProxyInstance::invokeDefaultMethod(ExecState* exec) +{ + return invoke(exec, InvokeDefault, 0, ArgList(exec)); +} + +bool ProxyInstance::supportsConstruct() const +{ + if (!m_instanceProxy) + return false; + + uint32_t requestID = m_instanceProxy->nextRequestID(); + + if (_WKPHNPObjectHasConstructMethod(m_instanceProxy->hostProxy()->port(), + m_instanceProxy->pluginID(), requestID, + m_objectID) != KERN_SUCCESS) + return false; + + auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); + if (reply.get() && reply->m_result) + return true; + + return false; +} + +JSValue ProxyInstance::invokeConstruct(ExecState* exec, const ArgList& args) +{ + return invoke(exec, Construct, 0, args); +} + +JSValue ProxyInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const +{ + if (hint == PreferString) + return stringValue(exec); + if (hint == PreferNumber) + return numberValue(exec); + return valueOf(exec); +} + +JSValue ProxyInstance::stringValue(ExecState* exec) const +{ + // FIXME: Implement something sensible. + return jsEmptyString(exec); +} + +JSValue ProxyInstance::numberValue(ExecState*) const +{ + // FIXME: Implement something sensible. + return jsNumber(0); +} + +JSValue ProxyInstance::booleanValue() const +{ + // FIXME: Implement something sensible. + return jsBoolean(false); +} + +JSValue ProxyInstance::valueOf(ExecState* exec) const +{ + return stringValue(exec); +} + +void ProxyInstance::getPropertyNames(ExecState* exec, PropertyNameArray& nameArray) +{ + if (!m_instanceProxy) + return; + + uint32_t requestID = m_instanceProxy->nextRequestID(); + + if (_WKPHNPObjectEnumerate(m_instanceProxy->hostProxy()->port(), m_instanceProxy->pluginID(), requestID, m_objectID) != KERN_SUCCESS) + return; + + auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID); + NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec); + if (!reply.get() || !reply->m_returnValue) + return; + + RetainPtr<NSArray*> array = [NSPropertyListSerialization propertyListFromData:(NSData *)reply->m_result.get() + mutabilityOption:NSPropertyListImmutable + format:0 + errorDescription:0]; + + for (NSNumber *number in array.get()) { + IdentifierRep* identifier = reinterpret_cast<IdentifierRep*>([number longLongValue]); + if (!IdentifierRep::isValid(identifier)) + continue; + + if (identifier->isString()) { + const char* str = identifier->string(); + nameArray.add(Identifier(JSDOMWindow::commonJSGlobalData(), stringToUString(String::fromUTF8WithLatin1Fallback(str, strlen(str))))); + } else + nameArray.add(Identifier::from(exec, identifier->number())); + } +} + +MethodList ProxyInstance::methodsNamed(const Identifier& identifier) +{ + if (!m_instanceProxy) + return MethodList(); + + // If we already have an entry in the map, use it. + MethodMap::iterator existingMapEntry = m_methods.find(identifier.impl()); + if (existingMapEntry != m_methods.end()) { + MethodList methodList; + if (existingMapEntry->second) + methodList.append(existingMapEntry->second); + return methodList; + } + + uint64_t methodName = reinterpret_cast<uint64_t>(_NPN_GetStringIdentifier(identifier.ascii().data())); + uint32_t requestID = m_instanceProxy->nextRequestID(); + + if (_WKPHNPObjectHasMethod(m_instanceProxy->hostProxy()->port(), + m_instanceProxy->pluginID(), requestID, + m_objectID, methodName) != KERN_SUCCESS) + return MethodList(); + + auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); + if (!reply.get()) + return MethodList(); + + if (!reply->m_result && !m_instanceProxy->hostProxy()->shouldCacheMissingPropertiesAndMethods()) + return MethodList(); + + // Add a new entry to the map unless an entry was added while we were in waitForReply. + pair<MethodMap::iterator, bool> mapAddResult = m_methods.add(identifier.impl(), 0); + if (mapAddResult.second && reply->m_result) + mapAddResult.first->second = new ProxyMethod(methodName); + + MethodList methodList; + if (mapAddResult.first->second) + methodList.append(mapAddResult.first->second); + return methodList; +} + +Field* ProxyInstance::fieldNamed(const Identifier& identifier) +{ + if (!m_instanceProxy) + return 0; + + // If we already have an entry in the map, use it. + FieldMap::iterator existingMapEntry = m_fields.find(identifier.impl()); + if (existingMapEntry != m_fields.end()) + return existingMapEntry->second; + + uint64_t propertyName = reinterpret_cast<uint64_t>(_NPN_GetStringIdentifier(identifier.ascii().data())); + uint32_t requestID = m_instanceProxy->nextRequestID(); + + if (_WKPHNPObjectHasProperty(m_instanceProxy->hostProxy()->port(), + m_instanceProxy->pluginID(), requestID, + m_objectID, propertyName) != KERN_SUCCESS) + return 0; + + auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); + if (!reply.get()) + return 0; + + if (!reply->m_result && !m_instanceProxy->hostProxy()->shouldCacheMissingPropertiesAndMethods()) + return 0; + + // Add a new entry to the map unless an entry was added while we were in waitForReply. + pair<FieldMap::iterator, bool> mapAddResult = m_fields.add(identifier.impl(), 0); + if (mapAddResult.second && reply->m_result) + mapAddResult.first->second = new ProxyField(propertyName); + return mapAddResult.first->second; +} + +JSC::JSValue ProxyInstance::fieldValue(ExecState* exec, const Field* field) const +{ + if (!m_instanceProxy) + return jsUndefined(); + + uint64_t serverIdentifier = static_cast<const ProxyField*>(field)->serverIdentifier(); + uint32_t requestID = m_instanceProxy->nextRequestID(); + + if (_WKPHNPObjectGetProperty(m_instanceProxy->hostProxy()->port(), + m_instanceProxy->pluginID(), requestID, + m_objectID, serverIdentifier) != KERN_SUCCESS) + return jsUndefined(); + + auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID); + NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec); + if (!reply.get() || !reply->m_returnValue) + return jsUndefined(); + + return m_instanceProxy->demarshalValue(exec, (char*)CFDataGetBytePtr(reply->m_result.get()), CFDataGetLength(reply->m_result.get())); +} + +void ProxyInstance::setFieldValue(ExecState* exec, const Field* field, JSValue value) const +{ + if (!m_instanceProxy) + return; + + uint64_t serverIdentifier = static_cast<const ProxyField*>(field)->serverIdentifier(); + uint32_t requestID = m_instanceProxy->nextRequestID(); + + data_t valueData; + mach_msg_type_number_t valueLength; + + m_instanceProxy->marshalValue(exec, value, valueData, valueLength); + m_instanceProxy->retainLocalObject(value); + kern_return_t kr = _WKPHNPObjectSetProperty(m_instanceProxy->hostProxy()->port(), + m_instanceProxy->pluginID(), requestID, + m_objectID, serverIdentifier, valueData, valueLength); + mig_deallocate(reinterpret_cast<vm_address_t>(valueData), valueLength); + if (m_instanceProxy) + m_instanceProxy->releaseLocalObject(value); + if (kr != KERN_SUCCESS) + return; + + auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); + NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(exec); +} + +void ProxyInstance::invalidate() +{ + ASSERT(m_instanceProxy); + + if (NetscapePluginHostProxy* hostProxy = m_instanceProxy->hostProxy()) + _WKPHNPObjectRelease(hostProxy->port(), + m_instanceProxy->pluginID(), m_objectID); + m_instanceProxy = 0; +} + +} // namespace WebKit + +#endif // USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API) + diff --git a/Source/WebKit/mac/Plugins/Hosted/ProxyRuntimeObject.h b/Source/WebKit/mac/Plugins/Hosted/ProxyRuntimeObject.h new file mode 100644 index 0000000..da6de88 --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/ProxyRuntimeObject.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#if USE(PLUGIN_HOST_PROCESS) + +#ifndef ProxyRuntimeObject_h +#define ProxyRuntimeObject_h + +#include <WebCore/runtime_object.h> + +namespace WebKit { + +class ProxyInstance; + +class ProxyRuntimeObject : public JSC::Bindings::RuntimeObject { +public: + ProxyRuntimeObject(JSC::ExecState*, JSC::JSGlobalObject*, PassRefPtr<ProxyInstance>); + virtual ~ProxyRuntimeObject(); + + ProxyInstance* getInternalProxyInstance() const; + + static JSC::Structure* createStructure(JSC::JSGlobalData& globalData, JSC::JSValue prototype) + { + return JSC::Structure::create(globalData, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), AnonymousSlotCount, &s_info); + } + + static const JSC::ClassInfo s_info; +}; + +} + +#endif +#endif diff --git a/Source/WebKit/mac/Plugins/Hosted/ProxyRuntimeObject.mm b/Source/WebKit/mac/Plugins/Hosted/ProxyRuntimeObject.mm new file mode 100644 index 0000000..c12d5cf --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/ProxyRuntimeObject.mm @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#if USE(PLUGIN_HOST_PROCESS) + +#include "runtime/ObjectPrototype.h" +#include <WebCore/JSDOMBinding.h> +#include "ProxyInstance.h" +#include "ProxyRuntimeObject.h" + +using namespace JSC; + +namespace WebKit { + + +const ClassInfo ProxyRuntimeObject::s_info = { "ProxyRuntimeObject", &RuntimeObject::s_info, 0, 0 }; + +ProxyRuntimeObject::ProxyRuntimeObject(ExecState* exec, JSGlobalObject* globalObject, PassRefPtr<ProxyInstance> instance) + // FIXME: deprecatedGetDOMStructure uses the prototype off of the wrong global object + // exec-globalData() is also likely wrong. + : RuntimeObject(exec, globalObject, WebCore::deprecatedGetDOMStructure<ProxyRuntimeObject>(exec), instance) +{ + ASSERT(inherits(&s_info)); +} + +ProxyRuntimeObject::~ProxyRuntimeObject() +{ +} + +ProxyInstance* ProxyRuntimeObject::getInternalProxyInstance() const +{ + return static_cast<ProxyInstance*>(getInternalInstance()); +} + + +} + +#endif diff --git a/Source/WebKit/mac/Plugins/Hosted/WebHostedNetscapePluginView.h b/Source/WebKit/mac/Plugins/Hosted/WebHostedNetscapePluginView.h new file mode 100644 index 0000000..1eb164d --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/WebHostedNetscapePluginView.h @@ -0,0 +1,67 @@ +/* + * 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. + * + * 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. + */ + +#if USE(PLUGIN_HOST_PROCESS) + +#import "WebBaseNetscapePluginView.h" +#import "WebKitSystemInterface.h" + +#import <wtf/RefPtr.h> + +namespace WebKit { + class HostedNetscapePluginStream; + class NetscapePluginInstanceProxy; +} + +@interface WebHostedNetscapePluginView : WebBaseNetscapePluginView<WebPluginManualLoader, WebPluginContainerCheckController> +{ + RetainPtr<NSArray> _attributeKeys; + RetainPtr<NSArray> _attributeValues; + + RetainPtr<CALayer> _pluginLayer; + WKSoftwareCARendererRef _softwareRenderer; + + NSSize _previousSize; + RefPtr<WebKit::NetscapePluginInstanceProxy> _proxy; +} + +- (id)initWithFrame:(NSRect)r + pluginPackage:(WebNetscapePluginPackage *)thePluginPackage + URL:(NSURL *)URL + baseURL:(NSURL *)baseURL + MIMEType:(NSString *)MIME + attributeKeys:(NSArray *)keys + attributeValues:(NSArray *)values + loadManually:(BOOL)loadManually + element:(PassRefPtr<WebCore::HTMLPlugInElement>)element; + +- (void)pluginHostDied; +- (CALayer *)pluginLayer; +- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithReason:(NPReason)reason; + +@end + +#endif // USE(PLUGIN_HOST_PROCESS) + diff --git a/Source/WebKit/mac/Plugins/Hosted/WebHostedNetscapePluginView.mm b/Source/WebKit/mac/Plugins/Hosted/WebHostedNetscapePluginView.mm new file mode 100644 index 0000000..d3cce46 --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/WebHostedNetscapePluginView.mm @@ -0,0 +1,534 @@ +/* + * 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. + * + * 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. + */ + +#if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API) + +#import "WebHostedNetscapePluginView.h" + +#import "HostedNetscapePluginStream.h" +#import "NetscapePluginInstanceProxy.h" +#import "NetscapePluginHostManager.h" +#import "NetscapePluginHostProxy.h" +#import "WebTextInputWindowController.h" +#import "WebFrameInternal.h" +#import "WebView.h" +#import "WebViewInternal.h" +#import "WebUIDelegate.h" + +#import <CoreFoundation/CoreFoundation.h> +#import <WebCore/BridgeJSC.h> +#import <WebCore/Frame.h> +#import <WebCore/FrameLoaderTypes.h> +#import <WebCore/FrameView.h> +#import <WebCore/HTMLPlugInElement.h> +#import <WebCore/RenderEmbeddedObject.h> +#import <WebCore/WebCoreObjCExtras.h> +#import <WebCore/runtime_root.h> +#import <runtime/InitializeThreading.h> +#import <wtf/Assertions.h> +#import <wtf/Threading.h> + +using namespace WebCore; +using namespace WebKit; + +extern "C" { +#include "WebKitPluginClientServer.h" +#include "WebKitPluginHost.h" +} + +@implementation WebHostedNetscapePluginView + ++ (void)initialize +{ + JSC::initializeThreading(); + WTF::initializeMainThreadToProcessMainThread(); +#ifndef BUILDING_ON_TIGER + WebCoreObjCFinalizeOnMainThread(self); +#endif + WKSendUserChangeNotifications(); +} + +- (id)initWithFrame:(NSRect)frame + pluginPackage:(WebNetscapePluginPackage *)pluginPackage + URL:(NSURL *)URL + baseURL:(NSURL *)baseURL + MIMEType:(NSString *)MIME + attributeKeys:(NSArray *)keys + attributeValues:(NSArray *)values + loadManually:(BOOL)loadManually + element:(PassRefPtr<WebCore::HTMLPlugInElement>)element +{ + self = [super initWithFrame:frame pluginPackage:pluginPackage URL:URL baseURL:baseURL MIMEType:MIME attributeKeys:keys attributeValues:values loadManually:loadManually element:element]; + if (!self) + return nil; + + return self; +} + +- (void)handleMouseMoved:(NSEvent *)event +{ + if (_isStarted && _proxy) + _proxy->mouseEvent(self, event, NPCocoaEventMouseMoved); +} + +- (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values +{ + ASSERT(!_attributeKeys); + ASSERT(!_attributeValues); + + _attributeKeys.adoptNS([keys copy]); + _attributeValues.adoptNS([values copy]); +} + +- (BOOL)createPlugin +{ + ASSERT(!_proxy); + + NSString *userAgent = [[self webView] userAgentForURL:_baseURL.get()]; + BOOL accleratedCompositingEnabled = false; +#if USE(ACCELERATED_COMPOSITING) + accleratedCompositingEnabled = [[[self webView] preferences] acceleratedCompositingEnabled]; +#endif + + _proxy = NetscapePluginHostManager::shared().instantiatePlugin([_pluginPackage.get() path], [_pluginPackage.get() pluginHostArchitecture], [_pluginPackage.get() bundleIdentifier], self, _MIMEType.get(), _attributeKeys.get(), _attributeValues.get(), userAgent, _sourceURL.get(), + _mode == NP_FULL, _isPrivateBrowsingEnabled, accleratedCompositingEnabled); + if (!_proxy) + return NO; + + if (_proxy->rendererType() == UseSoftwareRenderer) + _softwareRenderer = WKSoftwareCARendererCreate(_proxy->renderContextID()); + else { + _pluginLayer = WKMakeRenderLayer(_proxy->renderContextID()); + + if (accleratedCompositingEnabled && _proxy->rendererType() == UseAcceleratedCompositing) { + // FIXME: This code can be shared between WebHostedNetscapePluginView and WebNetscapePluginView. +#ifndef BUILDING_ON_LEOPARD + // Since this layer isn't going to be inserted into a view, we need to create another layer and flip its geometry + // in order to get the coordinate system right. + RetainPtr<CALayer> realPluginLayer(AdoptNS, _pluginLayer.releaseRef()); + + _pluginLayer.adoptNS([[CALayer alloc] init]); + _pluginLayer.get().bounds = realPluginLayer.get().bounds; + _pluginLayer.get().geometryFlipped = YES; + + realPluginLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable; + [_pluginLayer.get() addSublayer:realPluginLayer.get()]; +#endif + + // Eagerly enter compositing mode, since we know we'll need it. This avoids firing setNeedsStyleRecalc() + // for iframes that contain composited plugins at bad times. https://bugs.webkit.org/show_bug.cgi?id=39033 + core([self webFrame])->view()->enterCompositingMode(); + [self element]->setNeedsStyleRecalc(SyntheticStyleChange); + } else + self.wantsLayer = YES; + } + + // Update the window frame. + _proxy->windowFrameChanged([[self window] frame]); + + return YES; +} + +// FIXME: This method is an ideal candidate to move up to the base class +- (CALayer *)pluginLayer +{ + return _pluginLayer.get(); +} + +- (void)setLayer:(CALayer *)newLayer +{ + // FIXME: This should use the same implementation as WebNetscapePluginView (and move to the base class). + [super setLayer:newLayer]; + + if (_pluginLayer) + [newLayer addSublayer:_pluginLayer.get()]; +} + +- (void)privateBrowsingModeDidChange +{ + if (_proxy) + _proxy->privateBrowsingModeDidChange(_isPrivateBrowsingEnabled); +} + +- (void)loadStream +{ +} + +- (void)updateAndSetWindow +{ + if (!_proxy) + return; + + // The base coordinates of a window and it's contentView happen to be the equal at a userSpaceScaleFactor + // of 1. For non-1.0 scale factors this assumption is false. + NSView *windowContentView = [[self window] contentView]; + NSRect boundsInWindow = [self convertRect:[self bounds] toView:windowContentView]; + + NSRect visibleRectInWindow; + + // Core Animation plug-ins need to be updated (with a 0,0,0,0 clipRect) when + // moved to a background tab. We don't do this for Core Graphics plug-ins as + // older versions of Flash have historical WebKit-specific code that isn't + // compatible with this behavior. + BOOL shouldClipOutPlugin = _pluginLayer && [self shouldClipOutPlugin]; + if (!shouldClipOutPlugin) + visibleRectInWindow = [self actualVisibleRectInWindow]; + else + visibleRectInWindow = NSZeroRect; + + // Flip Y to convert NSWindow coordinates to top-left-based window coordinates. + float borderViewHeight = [[self currentWindow] frame].size.height; + boundsInWindow.origin.y = borderViewHeight - NSMaxY(boundsInWindow); + + if (!shouldClipOutPlugin) + visibleRectInWindow.origin.y = borderViewHeight - NSMaxY(visibleRectInWindow); + + _previousSize = boundsInWindow.size; + + _proxy->resize(boundsInWindow, visibleRectInWindow); + + CGRect bounds = NSRectToCGRect([self bounds]); + CGRect frame = NSRectToCGRect([self frame]); + + // We're not scaled, or in a subframe + CATransform3D scaleTransform = CATransform3DIdentity; + if (CGSizeEqualToSize(bounds.size, frame.size)) { + // We're in a subframe. Backing store is boundsInWindow.size. + if (boundsInWindow.size.width && boundsInWindow.size.height) + scaleTransform = CATransform3DMakeScale(frame.size.width / boundsInWindow.size.width, frame.size.height / boundsInWindow.size.height, 1); + } else { + // We're in the main frame with scaling. Need to mimic the frame/bounds scaling on Widgets. + if (frame.size.width && frame.size.height) + scaleTransform = CATransform3DMakeScale(bounds.size.width / frame.size.width, bounds.size.height / frame.size.height, 1); + } + + _pluginLayer.get().sublayerTransform = scaleTransform; +} + +- (void)windowFocusChanged:(BOOL)hasFocus +{ + if (_proxy) + _proxy->windowFocusChanged(hasFocus); +} + +- (BOOL)shouldStop +{ + if (!_proxy) + return YES; + + return _proxy->shouldStop(); +} + +- (void)destroyPlugin +{ + if (_proxy) { + if (_softwareRenderer) { + WKSoftwareCARendererDestroy(_softwareRenderer); + _softwareRenderer = 0; + } + + _proxy->destroy(); + _proxy = 0; + } + + _pluginLayer = 0; +} + +- (void)startTimers +{ + if (_proxy) + _proxy->startTimers(_isCompletelyObscured); +} + +- (void)stopTimers +{ + if (_proxy) + _proxy->stopTimers(); +} + +- (void)focusChanged +{ + if (_proxy) + _proxy->focusChanged(_hasFocus); +} + +- (void)windowFrameDidChange:(NSNotification *)notification +{ + if (_proxy && [self window]) + _proxy->windowFrameChanged([[self window] frame]); +} + +- (void)addWindowObservers +{ + [super addWindowObservers]; + + ASSERT([self window]); + + NSWindow *window = [self window]; + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter addObserver:self selector:@selector(windowFrameDidChange:) + name:NSWindowDidMoveNotification object:window]; + [notificationCenter addObserver:self selector:@selector(windowFrameDidChange:) + name:NSWindowDidResizeNotification object:window]; + + if (_proxy) + _proxy->windowFrameChanged([window frame]); + [self updateAndSetWindow]; +} + +- (void)removeWindowObservers +{ + [super removeWindowObservers]; + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter removeObserver:self name:NSWindowDidMoveNotification object:nil]; + [notificationCenter removeObserver:self name:NSWindowDidResizeNotification object:nil]; +} + +- (void)mouseDown:(NSEvent *)event +{ + if (_isStarted && _proxy) + _proxy->mouseEvent(self, event, NPCocoaEventMouseDown); +} + +- (void)mouseUp:(NSEvent *)event +{ + if (_isStarted && _proxy) + _proxy->mouseEvent(self, event, NPCocoaEventMouseUp); +} + +- (void)mouseDragged:(NSEvent *)event +{ + if (_isStarted && _proxy) + _proxy->mouseEvent(self, event, NPCocoaEventMouseDragged); +} + +- (void)handleMouseEntered:(NSEvent *)event +{ + // Set cursor to arrow. Plugins often handle cursor internally, but those that don't will just get this default one. + [[NSCursor arrowCursor] set]; + + if (_isStarted && _proxy) + _proxy->mouseEvent(self, event, NPCocoaEventMouseEntered); +} + +- (void)handleMouseExited:(NSEvent *)event +{ + if (_isStarted && _proxy) + _proxy->mouseEvent(self, event, NPCocoaEventMouseExited); + + // Set cursor back to arrow cursor. Because NSCursor doesn't know about changes that the plugin made, we could get confused about what we think the + // current cursor is otherwise. Therefore we have no choice but to unconditionally reset the cursor when the mouse exits the plugin. + // FIXME: This should be job of plugin host, see <rdar://problem/7654434>. + [[NSCursor arrowCursor] set]; +} + +- (void)scrollWheel:(NSEvent *)event +{ + bool processedEvent = false; + + if (_isStarted && _proxy) + processedEvent = _proxy->wheelEvent(self, event); + + if (!processedEvent) + [super scrollWheel:event]; +} + +- (NSTextInputContext *)inputContext +{ + return [[WebTextInputWindowController sharedTextInputWindowController] inputContext]; +} + +- (void)keyDown:(NSEvent *)event +{ + if (!_isStarted || !_proxy) + return; + + NSString *string = nil; + if ([[WebTextInputWindowController sharedTextInputWindowController] interpretKeyEvent:event string:&string]) { + if (string) + _proxy->insertText(string); + return; + } + + _proxy->keyEvent(self, event, NPCocoaEventKeyDown); +} + +- (void)keyUp:(NSEvent *)event +{ + if (_isStarted && _proxy) + _proxy->keyEvent(self, event, NPCocoaEventKeyUp); +} + +- (void)flagsChanged:(NSEvent *)event +{ + if (_isStarted && _proxy) + _proxy->flagsChanged(event); +} + +- (void)sendModifierEventWithKeyCode:(int)keyCode character:(char)character +{ + if (_isStarted && _proxy) + _proxy->syntheticKeyDownWithCommandModifier(keyCode, character); +} + +- (void)pluginHostDied +{ + if (_element->renderer() && _element->renderer()->isEmbeddedObject()) { + // FIXME: The renderer could also be a RenderApplet, we should handle that. + RenderEmbeddedObject* renderer = toRenderEmbeddedObject(_element->renderer()); + renderer->setShowsCrashedPluginIndicator(); + } + + _pluginLayer = nil; + _proxy = 0; + + // No need for us to be layer backed anymore + self.wantsLayer = NO; + + [self invalidatePluginContentRect:[self bounds]]; +} + +- (void)visibleRectDidChange +{ + [super visibleRectDidChange]; + WKSyncSurfaceToView(self); +} + +- (void)drawRect:(NSRect)rect +{ + if (_cachedSnapshot) { + NSRect sourceRect = { NSZeroPoint, [_cachedSnapshot.get() size] }; + [_cachedSnapshot.get() drawInRect:[self bounds] fromRect:sourceRect operation:NSCompositeSourceOver fraction:1]; + return; + } + + if (_proxy) { + if (_softwareRenderer) { + if ([NSGraphicsContext currentContextDrawingToScreen]) { + WKSoftwareCARendererRender(_softwareRenderer, (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort], NSRectToCGRect(rect)); + _proxy->didDraw(); + } else + _proxy->print(reinterpret_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]), [self bounds].size.width, [self bounds].size.height); + } else if (_snapshotting && [self supportsSnapshotting]) { + _proxy->snapshot(reinterpret_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]), [self bounds].size.width, [self bounds].size.height); + } + + return; + } +} + +- (PassRefPtr<JSC::Bindings::Instance>)createPluginBindingsInstance:(PassRefPtr<JSC::Bindings::RootObject>)rootObject +{ + if (!_proxy) + return 0; + + return _proxy->createBindingsInstance(rootObject); +} + +- (void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response +{ + ASSERT(_loadManually); + if (!_proxy) + return; + + ASSERT(!_proxy->manualStream()); + + _proxy->setManualStream(HostedNetscapePluginStream::create(_proxy.get(), core([self webFrame])->loader())); + _proxy->manualStream()->startStreamWithResponse(response); +} + +- (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data +{ + ASSERT(_loadManually); + if (!_proxy) + return; + + if (HostedNetscapePluginStream* manualStream = _proxy->manualStream()) + manualStream->didReceiveData(0, static_cast<const char*>([data bytes]), [data length]); +} + +- (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error +{ + ASSERT(_loadManually); + if (!_proxy) + return; + + if (HostedNetscapePluginStream* manualStream = _proxy->manualStream()) + manualStream->didFail(0, error); +} + +- (void)pluginViewFinishedLoading:(NSView *)pluginView +{ + ASSERT(_loadManually); + if (!_proxy) + return; + + if (HostedNetscapePluginStream* manualStream = _proxy->manualStream()) + manualStream->didFinishLoading(0); +} + +- (void)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)webPluginContainerCheck +{ + ASSERT([webPluginContainerCheck isKindOfClass:[WebPluginContainerCheck class]]); + + id contextInfo = [webPluginContainerCheck contextInfo]; + ASSERT([contextInfo isKindOfClass:[NSNumber class]]); + + if (!_proxy) + return; + + uint32_t checkID = [(NSNumber *)contextInfo unsignedIntValue]; + _proxy->cancelCheckIfAllowedToLoadURL(checkID); +} + +- (void)_containerCheckResult:(PolicyAction)policy contextInfo:(id)contextInfo +{ + ASSERT([contextInfo isKindOfClass:[NSNumber class]]); + if (!_proxy) + return; + + uint32_t checkID = [(NSNumber *)contextInfo unsignedIntValue]; + _proxy->checkIfAllowedToLoadURLResult(checkID, (policy == PolicyUse)); +} + +- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithReason:(NPReason)reason +{ + if (_isStarted && _proxy) + _proxy->webFrameDidFinishLoadWithReason(webFrame, reason); +} + +- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithError:(NSError *)error +{ + NPReason reason = NPRES_DONE; + if (error) + reason = HostedNetscapePluginStream::reasonForError(error); + [self webFrame:webFrame didFinishLoadWithReason:reason]; +} + +@end + +#endif // USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API) diff --git a/Source/WebKit/mac/Plugins/Hosted/WebKitPluginAgent.defs b/Source/WebKit/mac/Plugins/Hosted/WebKitPluginAgent.defs new file mode 100644 index 0000000..0f332b6 --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/WebKitPluginAgent.defs @@ -0,0 +1,45 @@ +/* + * 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. + * + * 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. + */ + +#include <WebKit/WebKitPluginHostTypes.defs> + +subsystem WebKitPluginAgent 100; + +serverprefix WKPA; +userprefix _WKPA; + +routine CheckInApplication(serverPort :mach_port_t; + ServerAuditToken token :audit_token_t; + applicationName :application_name_t; + out pluginVendorPort :mach_port_make_send_t); + +routine SpawnPluginHost(pluginVendorPort :mach_port_t; + sreplyport _replyPort :mach_port_make_send_once_t; + options :plist_bytes_t; + out pluginHostPort: mach_port_move_send_t); + +routine CheckInPluginHost(serverPort :mach_port_t; + pluginHostPort :mach_port_move_send_t; + ServerAuditToken token :audit_token_t); diff --git a/Source/WebKit/mac/Plugins/Hosted/WebKitPluginAgentReply.defs b/Source/WebKit/mac/Plugins/Hosted/WebKitPluginAgentReply.defs new file mode 100644 index 0000000..c802b3d --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/WebKitPluginAgentReply.defs @@ -0,0 +1,38 @@ +/* + * 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. + * + * 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. + */ + +#include <WebKit/WebKitPluginHostTypes.defs> + +subsystem WebKitPluginAgentReply 200; + +serverprefix WKPA; +userprefix _WKPA; + +skip; // CheckInApplication + +simpleroutine SpawnPluginHostReply(_replyPort :mach_port_move_send_once_t; + in pluginHostPort: mach_port_move_send_t); + +skip; // CheckInPluginHost diff --git a/Source/WebKit/mac/Plugins/Hosted/WebKitPluginClient.defs b/Source/WebKit/mac/Plugins/Hosted/WebKitPluginClient.defs new file mode 100644 index 0000000..15fc5fa --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/WebKitPluginClient.defs @@ -0,0 +1,238 @@ +/* + * 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. + * + * 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. + */ + +#include <WebKit/WebKitPluginHostTypes.defs> + +// FIXME: Come up with a better name. +subsystem WebKitPluginClient 300; + +serverprefix WK; +userprefix _WK; + +simpleroutine PCStatusText(clientPort :mach_port_t; + pluginID :uint32_t; + text :data_t); + +routine PCLoadURL(clientPort :mach_port_t; + pluginID :uint32_t; + url :data_t; + target :data_t; + postData :data_t; + flags: uint32_t; + out resultCode :uint16_t; + out requestID :uint32_t); + +simpleroutine PCCancelLoadURL(clientPort :mach_port_t; + pluginID :uint32_t; + streamID :uint32_t; + reason :int16_t); + +simpleroutine PCInvalidateRect(clientPort :mach_port_t; + pluginID :uint32_t; + x :double; + y :double; + width :double; + height :double); + +routine PCGetCookies(clientPort :mach_port_t; + pluginID :uint32_t; + url :data_t; + out returnValue :boolean_t; + out cookies :data_t, dealloc); + +routine PCSetCookies(clientPort :mach_port_t; + pluginID :uint32_t; + url :data_t; + cookies :data_t; + out returnValue :boolean_t); + +routine PCGetProxy(clientPort :mach_port_t; + pluginID :uint32_t; + url :data_t; + out returnValue :boolean_t; + out proxy :data_t, dealloc); + +routine PCGetAuthenticationInfo(clientPort :mach_port_t; + pluginID :uint32_t; + protocol :data_t; + host :data_t; + port :uint32_t; + scheme :data_t; + realm :data_t; + out returnValue :boolean_t; + out username :data_t, dealloc; + out password :data_t, dealloc); + +routine PCConvertPoint(clientPort :mach_port_t; + pluginID :uint32_t; + sourceX :double; + sourceY :double; + sourceSpace :uint32_t; + destSpace :uint32_t; + out returnValue :boolean_t; + out destX :double; + out destY :double); + +// NPRuntime + +routine PCGetStringIdentifier(clientPort :mach_port_t; + name :data_t; + out identifier :uint64_t); + +routine PCGetIntIdentifier(clientPort :mach_port_t; + value :int32_t; + out identifier: uint64_t); + +routine PCGetWindowNPObject(clientPort :mach_port_t; + pluginID :uint32_t; + out objectID :uint32_t); +routine PCGetPluginElementNPObject(clientPort :mach_port_t; + pluginID :uint32_t; + out objectID :uint32_t); + +routine PCForgetBrowserObject(clientPort :mach_port_t; + pluginID :uint32_t; + objectID :uint32_t); + +simpleroutine PCEvaluate(clientPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + objectID :uint32_t; + script :data_t; + allowPopups :boolean_t); + +simpleroutine PCInvoke(clientPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + objectID :uint32_t; + methodNameIdentifier :uint64_t; + arguments :data_t); + +simpleroutine PCInvokeDefault(clientPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + objectID :uint32_t; + arguments :data_t); + +routine PCConstruct(clientPort :mach_port_t; + pluginID :uint32_t; + objectID :uint32_t; + arguments :data_t; + out returnValue :boolean_t; + out result :data_t, dealloc); + +simpleroutine PCGetProperty(clientPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + objectID :uint32_t; + propertyNameIdentifier :uint64_t); + +simpleroutine PCSetProperty(clientPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + objectID :uint32_t; + propertyNameIdentifier :uint64_t; + value :data_t); + +simpleroutine PCRemoveProperty(clientPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + objectID :uint32_t; + propertyNameIdentifier :uint64_t); + +simpleroutine PCHasProperty(clientPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + objectID :uint32_t; + propertyNameIdentifier :uint64_t); + +simpleroutine PCHasMethod(clientPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + objectID :uint32_t; + methodNameIdentifier :uint64_t); + +routine PCIdentifierInfo(clientPort :mach_port_t; + identifier :uint64_t; + out info :data_t, dealloc); + +simpleroutine PCEnumerate(clientPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + objectID :uint32_t); + +// Misc + +simpleroutine PCSetMenuBarVisible(clientPort :mach_port_t; + visible :boolean_t); + +simpleroutine PCSetModal(clientPort :mach_port_t; + modal :boolean_t); + +routine PCCheckIfAllowedToLoadURL(clientPort :mach_port_t; + pluginID :uint32_t; + url :data_t; + target :data_t; + out checkID :uint32_t); + +simpleroutine PCCancelCheckIfAllowedToLoadURL(clientPort :mach_port_t; + pluginID :uint32_t; + checkID :uint32_t); + +routine PCResolveURL(clientPort :mach_port_t; + pluginID :uint32_t; + url :data_t; + target :data_t; + out resolvedURL :data_t, dealloc); + +// Replies +simpleroutine PCInstantiatePluginReply(clientPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + result :kern_return_t; + renderContextID :uint32_t; + rendererType :uint32_t); + +simpleroutine PCGetScriptableNPObjectReply(clientPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + objectID :uint32_t); + +simpleroutine PCBooleanReply(clientPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + result :boolean_t); + +simpleroutine PCBooleanAndDataReply(clientPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + returnValue :boolean_t; + result :data_t); + +simpleroutine PCSetFullscreenWindowIsShowing(clientPort :mach_port_t; + isShowing :boolean_t); + +simpleroutine PCSetException(clientPort :mach_port_t; + message :data_t); diff --git a/Source/WebKit/mac/Plugins/Hosted/WebKitPluginHost.defs b/Source/WebKit/mac/Plugins/Hosted/WebKitPluginHost.defs new file mode 100644 index 0000000..fa808ac --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/WebKitPluginHost.defs @@ -0,0 +1,247 @@ +/* + * 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. + * + * 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. + */ + +#include <WebKit/WebKitPluginHostTypes.defs> + +subsystem WebKitPluginHost 300; + +serverprefix WK; +userprefix _WK; + +routine PHCheckInWithPluginHost(pluginHostPort :mach_port_t; + options :plist_bytes_t; + clientPort :mach_port_make_send_t; + clientPSNHigh :uint32_t; + clientPSNLow :uint32_t; + renderPort :mach_port_copy_send_t; + out pluginHostPSNHigh :uint32_t; + out pluginHostPSNLow :uint32_t); + +simpleroutine PHInstantiatePlugin(pluginHostPort :mach_port_t; + requestID :uint32_t; + options :plist_bytes_t; + pluginID :uint32_t); + +simpleroutine PHResizePluginInstance(pluginHostPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + pluginX :double; + pluginY :double; + pluginWidth :double; + pluginHeight :double; + clipX :double; + clipY :double; + clipWidth :double; + clipHeight :double); + +simpleroutine PHPluginInstanceFocusChanged(pluginHostPort :mach_port_t; + pluginID :uint32_t; + hasFocus :boolean_t); +simpleroutine PHPluginInstanceWindowFocusChanged(pluginHostPort :mach_port_t; + pluginID :uint32_t; + hasFocus :boolean_t); +simpleroutine PHPluginInstanceWindowFrameChanged(pluginHostPort :mach_port_t; + pluginID :uint32_t; + x :double; + y :double; + width :double; + height :double; + maxScreenY :double); + +simpleroutine PHPluginInstanceMouseEvent(pluginHostPort :mach_port_t; + pluginID :uint32_t; + timestamp :double; + eventType :uint32_t; + modifierFlags :uint32_t; + pluginX :double; + pluginY :double; + screenX :double; + screenY :double; + maxScreenY :double; + buttonNumber :int32_t; + clickCount :int32_t; + deltaX :double; + deltaY :double; + deltaZ: double); + +simpleroutine PHPluginInstanceKeyboardEvent(pluginHostPort :mach_port_t; + pluginID :uint32_t; + timestamp :double; + eventType :uint32_t; + modifierFlags :uint32_t; + characters :data_t; + charactersIgnoringModifiers :data_t; + isARepeat :boolean_t; + keyCode :uint16_t; + keyChar :uint8_t); + +simpleroutine PHPluginInstanceWheelEvent(pluginHostPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + timestamp :double; + modifierFlags :uint32_t; + pluginX :double; + pluginY :double; + buttonNumber :int32_t; + deltaX :double; + deltaY :double; + deltaZ: double); + +simpleroutine PHPluginInstanceInsertText(pluginHostPort :mach_port_t; + pluginID :uint32_t; + text :data_t); + +simpleroutine PHPluginInstanceStartTimers(pluginHostPort :mach_port_t; + pluginID :uint32_t; + throttleTimers :boolean_t); +simpleroutine PHPluginInstanceStopTimers(pluginHostPort :mach_port_t; + pluginID :uint32_t); + +simpleroutine PHPluginInstancePrint(pluginHostPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + width :uint32_t; + height :uint32_t); + +simpleroutine PHDestroyPluginInstance(pluginHostPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t); + +simpleroutine PHCheckIfAllowedToLoadURLResult(clientPort :mach_port_t; + pluginID :uint32_t; + checkID :uint32_t; + result :boolean_t); + +// Streams +simpleroutine PHStartStream(pluginHostPort :mach_port_t; + pluginID :uint32_t; + streamID :uint32_t; + responseURL :data_t; + expectedContentLength :int64_t; + lastModifiedTimeInterval :double; + mimeType :data_t; + headers :data_t); + +simpleroutine PHStreamDidReceiveData(pluginHostPort :mach_port_t; + pluginID :uint32_t; + streamID :uint32_t; + data :data_t); + +simpleroutine PHStreamDidFinishLoading(pluginHostPort :mach_port_t; + pluginID :uint32_t; + streamID :uint32_t); + +simpleroutine PHStreamDidFail(pluginHostPort :mach_port_t; + pluginID :uint32_t; + streamID :uint32_t; + reason :int16_t); + +simpleroutine PHLoadURLNotify(pluginHostPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + reason :int16_t); + +// NPRuntime + +simpleroutine PHGetScriptableNPObject(pluginHostPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t); + +simpleroutine PHNPObjectHasProperty(pluginHostPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + objectID :uint32_t; + propertyName :uint64_t); + +simpleroutine PHNPObjectHasMethod(pluginHostPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + objectID :uint32_t; + methodName :uint64_t); + +simpleroutine PHNPObjectInvoke(pluginHostPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + objectID :uint32_t; + invokeType :uint32_t; + methodName :uint64_t; + arguments :data_t); + +simpleroutine PHNPObjectHasInvokeDefaultMethod(pluginHostPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + objectID :uint32_t); + +simpleroutine PHNPObjectHasConstructMethod(pluginHostPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + objectID :uint32_t); + +simpleroutine PHNPObjectGetProperty(pluginHostPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + objectID :uint32_t; + propertyName :uint64_t); + +simpleroutine PHNPObjectSetProperty(pluginHostPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + objectID :uint32_t; + propertyName :uint64_t; + value :data_t); + +simpleroutine PHNPObjectRelease(pluginHostPort :mach_port_t; + pluginID :uint32_t; + objectID :uint32_t); + +simpleroutine PHNPObjectEnumerate(pluginHostPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + objectID :uint32_t); + +// Replies + +simpleroutine PHBooleanReply(clientPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + result :boolean_t); + +simpleroutine PHBooleanAndDataReply(pluginHostPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + returnValue :boolean_t; + result :data_t); + +simpleroutine PHPluginInstanceDidDraw(pluginHostPort :mach_port_t; + pluginID :uint32_t); +simpleroutine PHPluginInstancePrivateBrowsingModeDidChange(pluginHostPort :mach_port_t; + pluginID :uint32_t; + privateBrowsingEnabled :boolean_t); + +simpleroutine PHPluginInstanceSnapshot(pluginHostPort :mach_port_t; + pluginID :uint32_t; + requestID :uint32_t; + width :uint32_t; + height :uint32_t); diff --git a/Source/WebKit/mac/Plugins/Hosted/WebKitPluginHostTypes.defs b/Source/WebKit/mac/Plugins/Hosted/WebKitPluginHostTypes.defs new file mode 100644 index 0000000..ee53520 --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/WebKitPluginHostTypes.defs @@ -0,0 +1,33 @@ +/* + * 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. + * + * 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. + */ + +#include <mach/std_types.defs> +#include <mach/mach_types.defs> + +import <WebKit/WebKitPluginHostTypes.h>; + +type plist_bytes_t = ^array [] of uint8_t; +type application_name_t = ^array [] of uint8_t; +type data_t = ^array [] of char; diff --git a/Source/WebKit/mac/Plugins/Hosted/WebKitPluginHostTypes.h b/Source/WebKit/mac/Plugins/Hosted/WebKitPluginHostTypes.h new file mode 100644 index 0000000..0bac2bc --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/WebKitPluginHostTypes.h @@ -0,0 +1,67 @@ +/* + * 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. + * + * 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. + */ + +#ifndef WebKitPluginHostTypes_h +#define WebKitPluginHostTypes_h + +typedef uint8_t* plist_bytes_t; +typedef uint8_t* application_name_t; + +typedef char* data_t; + +#ifndef __MigTypeCheck +#define __MigTypeCheck 1 +#endif + +enum LoadURLFlags { + IsPost = 1 << 0, + PostDataIsFile = 1 << 1, + AllowHeadersInPostData = 1 << 2, + AllowPopups = 1 << 3, +}; + +enum InvokeType { + Invoke, + InvokeDefault, + Construct +}; + +enum ValueType { + VoidValueType, + NullValueType, + BoolValueType, + DoubleValueType, + StringValueType, + JSObjectValueType, + NPObjectValueType +}; + +enum RendererType { + UseAcceleratedCompositing, + UseSoftwareRenderer, + UseLayerBackedView +}; + +#endif // WebKitPluginHostTypes_h diff --git a/Source/WebKit/mac/Plugins/Hosted/WebTextInputWindowController.h b/Source/WebKit/mac/Plugins/Hosted/WebTextInputWindowController.h new file mode 100644 index 0000000..9f036ee --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/WebTextInputWindowController.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. + */ + +#if USE(PLUGIN_HOST_PROCESS) + +#ifndef WebTextInputWindowController_h +#define WebTextInputWindowController_h + +@class WebTextInputPanel; + +@interface WebTextInputWindowController : NSObject { + WebTextInputPanel *_panel; +} + ++ (WebTextInputWindowController *)sharedTextInputWindowController; + +- (NSTextInputContext *)inputContext; +- (BOOL)interpretKeyEvent:(NSEvent *)event string:(NSString **)string; + +@end + +#endif // WebTextInputWindowController_h + +#endif // USE(PLUGIN_HOST_PROCESS) diff --git a/Source/WebKit/mac/Plugins/Hosted/WebTextInputWindowController.m b/Source/WebKit/mac/Plugins/Hosted/WebTextInputWindowController.m new file mode 100644 index 0000000..8b59d34 --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/WebTextInputWindowController.m @@ -0,0 +1,168 @@ +/* + * 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. + */ + +#if USE(PLUGIN_HOST_PROCESS) + +#import "WebTextInputWindowController.h" + +#import <WebKitSystemInterface.h> + +@interface WebTextInputPanel : NSPanel { + NSTextView *_inputTextView; +} + +- (NSTextInputContext *)_inputContext; +- (BOOL)_interpretKeyEvent:(NSEvent *)event string:(NSString **)string; + +@end + +#define inputWindowHeight 20 + +@implementation WebTextInputPanel + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [_inputTextView release]; + + [super dealloc]; +} + +- (id)init +{ + self = [super initWithContentRect:NSZeroRect styleMask:WKGetInputPanelWindowStyle() backing:NSBackingStoreBuffered defer:YES]; + if (!self) + return nil; + + // Set the frame size. + NSRect visibleFrame = [[NSScreen mainScreen] visibleFrame]; + NSRect frame = NSMakeRect(visibleFrame.origin.x, visibleFrame.origin.y, visibleFrame.size.width, inputWindowHeight); + + [self setFrame:frame display:NO]; + + _inputTextView = [[NSTextView alloc] initWithFrame:[self.contentView frame]]; + _inputTextView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable | NSViewMaxXMargin | NSViewMinXMargin | NSViewMaxYMargin | NSViewMinYMargin; + + NSScrollView* scrollView = [[NSScrollView alloc] initWithFrame:[self.contentView frame]]; + scrollView.documentView = _inputTextView; + self.contentView = scrollView; + [scrollView release]; + + [self setFloatingPanel:YES]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(_keyboardInputSourceChanged:) + name:NSTextInputContextKeyboardSelectionDidChangeNotification + object:nil]; + + return self; +} + +- (void)_keyboardInputSourceChanged:(NSNotification *)notification +{ + [_inputTextView setString:@""]; + [self orderOut:nil]; +} + +- (BOOL)_interpretKeyEvent:(NSEvent *)event string:(NSString **)string +{ + BOOL hadMarkedText = [_inputTextView hasMarkedText]; + + *string = nil; + + // Let TSM know that a bottom input window would be created for marked text. + EventRef carbonEvent = (EventRef)[event eventRef]; + if (carbonEvent) { + Boolean ignorePAH = true; + SetEventParameter(carbonEvent, 'iPAH', typeBoolean, sizeof(ignorePAH), &ignorePAH); + } + + if (![[_inputTextView inputContext] handleEvent:event]) + return NO; + + if ([_inputTextView hasMarkedText]) { + // Don't show the input method window for dead keys + if ([[event characters] length] > 0) + [self orderFront:nil]; + + return YES; + } + + if (hadMarkedText) { + [self orderOut:nil]; + + NSString *text = [[_inputTextView textStorage] string]; + if ([text length] > 0) + *string = [[text copy] autorelease]; + } + + [_inputTextView setString:@""]; + return hadMarkedText; +} + +- (NSTextInputContext *)_inputContext +{ + return [_inputTextView inputContext]; +} + +@end + +@implementation WebTextInputWindowController + ++ (WebTextInputWindowController *)sharedTextInputWindowController +{ + static WebTextInputWindowController *textInputWindowController; + if (!textInputWindowController) + textInputWindowController = [[WebTextInputWindowController alloc] init]; + + return textInputWindowController; +} + +- (id)init +{ + self = [super init]; + if (!self) + return nil; + + _panel = [[WebTextInputPanel alloc] init]; + + return self; +} + +- (NSTextInputContext *)inputContext +{ + return [_panel _inputContext]; +} + +- (BOOL)interpretKeyEvent:(NSEvent *)event string:(NSString **)string +{ + return [_panel _interpretKeyEvent:event string:string]; +} + +@end + +#endif // USE(PLUGIN_HOST_PROCESS) + diff --git a/Source/WebKit/mac/Plugins/WebBaseNetscapePluginView.h b/Source/WebKit/mac/Plugins/WebBaseNetscapePluginView.h new file mode 100644 index 0000000..448b750 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebBaseNetscapePluginView.h @@ -0,0 +1,160 @@ +/* + * Copyright (C) 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. + */ + +#if ENABLE(NETSCAPE_PLUGIN_API) +#import <Cocoa/Cocoa.h> + +#import "WebNetscapePluginPackage.h" +#import "WebPluginContainerCheck.h" +#import <wtf/Forward.h> +#import <wtf/OwnPtr.h> +#import <wtf/PassRefPtr.h> +#import <wtf/RefPtr.h> +#import <wtf/RetainPtr.h> + +@class DOMElement; +@class WebDataSource; +@class WebFrame; +@class WebView; + +namespace WebCore { + class HTMLPlugInElement; +} + +class WebHaltablePlugin; + +// Also declared in WebCore/WidgetMac.mm +@interface NSView (Widget) +- (void)visibleRectDidChange; +@end + +@interface WebBaseNetscapePluginView : NSView +{ + RetainPtr<WebNetscapePluginPackage> _pluginPackage; + + WebFrame *_webFrame; + + int _mode; + + BOOL _triedAndFailedToCreatePlugin; + BOOL _loadManually; + BOOL _shouldFireTimers; + BOOL _isStarted; + BOOL _hasFocus; + BOOL _isCompletelyObscured; + BOOL _isPrivateBrowsingEnabled; + BOOL _isHalted; + BOOL _hasBeenHalted; + BOOL _snapshotting; + + RefPtr<WebCore::HTMLPlugInElement> _element; + RetainPtr<NSString> _MIMEType; + RetainPtr<NSURL> _baseURL; + RetainPtr<NSURL> _sourceURL; + RetainPtr<NSImage> _cachedSnapshot; + + OwnPtr<WebHaltablePlugin> _haltable; + + NSTrackingRectTag _trackingTag; +} + +- (id)initWithFrame:(NSRect)r + pluginPackage:(WebNetscapePluginPackage *)thePluginPackage + URL:(NSURL *)URL + baseURL:(NSURL *)baseURL + MIMEType:(NSString *)MIME + attributeKeys:(NSArray *)keys + attributeValues:(NSArray *)values + loadManually:(BOOL)loadManually + element:(PassRefPtr<WebCore::HTMLPlugInElement>)element; + +- (WebNetscapePluginPackage *)pluginPackage; + +- (NSURL *)URLWithCString:(const char *)URLCString; +- (NSMutableURLRequest *)requestWithURLCString:(const char *)URLCString; + +// Subclasses must override these. +// The "handle" prefix is needed to avoid overriding NSView methods. +- (void)handleMouseMoved:(NSEvent *)event; +- (void)handleMouseEntered:(NSEvent *)event; +- (void)handleMouseExited:(NSEvent *)event; +- (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values; +- (void)focusChanged; + +- (WebFrame *)webFrame; +- (WebDataSource *)dataSource; +- (WebView *)webView; +- (NSWindow *)currentWindow; +- (WebCore::HTMLPlugInElement*)element; + +- (void)removeTrackingRect; +- (void)resetTrackingRect; + +- (void)stopTimers; +- (void)startTimers; +- (void)restartTimers; + +- (void)start; +- (void)stop; + +- (void)halt; +- (void)resumeFromHalt; +- (BOOL)isHalted; +- (BOOL)hasBeenHalted; + +- (void)addWindowObservers; +- (void)removeWindowObservers; +- (BOOL)shouldClipOutPlugin; +- (BOOL)inFlatteningPaint; + +- (BOOL)supportsSnapshotting; +- (void)cacheSnapshot; +- (void)clearCachedSnapshot; + +- (BOOL)convertFromX:(double)sourceX andY:(double)sourceY space:(NPCoordinateSpace)sourceSpace + toX:(double *)destX andY:(double *)destY space:(NPCoordinateSpace)destSpace; +- (WTF::CString)resolvedURLStringForURL:(const char*)url target:(const char*)target; + +- (void)invalidatePluginContentRect:(NSRect)rect; + +- (NSRect)actualVisibleRectInWindow; // takes transforms into account. + +#ifndef BUILDING_ON_TIGER +- (CALayer *)pluginLayer; +#endif + +@end + + +namespace WebKit { +bool getAuthenticationInfo(const char* protocolStr, const char* hostStr, int32_t port, const char* schemeStr, const char* realmStr, + WTF::CString& username, WTF::CString& password); +} + +#endif + diff --git a/Source/WebKit/mac/Plugins/WebBaseNetscapePluginView.mm b/Source/WebKit/mac/Plugins/WebBaseNetscapePluginView.mm new file mode 100644 index 0000000..f1eecde --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebBaseNetscapePluginView.mm @@ -0,0 +1,1045 @@ +/* + * 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. + */ + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#import "WebBaseNetscapePluginView.h" + +#import "WebFrameInternal.h" +#import "WebKitLogging.h" +#import "WebKitNSStringExtras.h" +#import "WebKitSystemInterface.h" +#import "WebPluginContainerCheck.h" +#import "WebNetscapeContainerCheckContextInfo.h" +#import "WebNSURLExtras.h" +#import "WebNSURLRequestExtras.h" +#import "WebView.h" +#import "WebViewInternal.h" + +#import <WebCore/AuthenticationMac.h> +#import <WebCore/BitmapImage.h> +#import <WebCore/Credential.h> +#import <WebCore/CredentialStorage.h> +#import <WebCore/Document.h> +#import <WebCore/Element.h> +#import <WebCore/Frame.h> +#import <WebCore/FrameLoader.h> +#import <WebCore/HTMLPlugInElement.h> +#import <WebCore/HaltablePlugin.h> +#import <WebCore/Page.h> +#import <WebCore/ProtectionSpace.h> +#import <WebCore/RenderView.h> +#import <WebCore/RenderWidget.h> +#import <WebCore/WebCoreObjCExtras.h> +#import <WebKit/DOMPrivate.h> +#import <runtime/InitializeThreading.h> +#import <wtf/Assertions.h> +#import <wtf/Threading.h> +#import <wtf/text/CString.h> + +#define LoginWindowDidSwitchFromUserNotification @"WebLoginWindowDidSwitchFromUserNotification" +#define LoginWindowDidSwitchToUserNotification @"WebLoginWindowDidSwitchToUserNotification" + +static const NSTimeInterval ClearSubstituteImageDelay = 0.5; + +using namespace WebCore; + +class WebHaltablePlugin : public HaltablePlugin { +public: + WebHaltablePlugin(WebBaseNetscapePluginView* view) + : m_view(view) + { + } + +private: + virtual void halt(); + virtual void restart(); + virtual Node* node() const; + virtual bool isWindowed() const; + virtual String pluginName() const; + + WebBaseNetscapePluginView* m_view; +}; + +void WebHaltablePlugin::halt() +{ + [m_view halt]; +} + +void WebHaltablePlugin::restart() +{ + [m_view resumeFromHalt]; +} + +Node* WebHaltablePlugin::node() const +{ + return [m_view element]; +} + +bool WebHaltablePlugin::isWindowed() const +{ + return false; +} + +String WebHaltablePlugin::pluginName() const +{ + return [[m_view pluginPackage] pluginInfo].name; +} + +@implementation WebBaseNetscapePluginView + ++ (void)initialize +{ + JSC::initializeThreading(); + WTF::initializeMainThreadToProcessMainThread(); +#ifndef BUILDING_ON_TIGER + WebCoreObjCFinalizeOnMainThread(self); +#endif + WKSendUserChangeNotifications(); +} + +- (id)initWithFrame:(NSRect)frame + pluginPackage:(WebNetscapePluginPackage *)pluginPackage + URL:(NSURL *)URL + baseURL:(NSURL *)baseURL + MIMEType:(NSString *)MIME + attributeKeys:(NSArray *)keys + attributeValues:(NSArray *)values + loadManually:(BOOL)loadManually + element:(PassRefPtr<WebCore::HTMLPlugInElement>)element +{ + self = [super initWithFrame:frame]; + if (!self) + return nil; + + _pluginPackage = pluginPackage; + _element = element; + _sourceURL.adoptNS([URL copy]); + _baseURL.adoptNS([baseURL copy]); + _MIMEType.adoptNS([MIME copy]); + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + // Enable "kiosk mode" when instantiating the QT plug-in inside of Dashboard. See <rdar://problem/6878105> + if ([[[NSBundle mainBundle] bundleIdentifier] isEqualToString:@"com.apple.dashboard.client"] && + [_pluginPackage.get() bundleIdentifier] == "com.apple.QuickTime Plugin.plugin") { + RetainPtr<NSMutableArray> mutableKeys(AdoptNS, [keys mutableCopy]); + RetainPtr<NSMutableArray> mutableValues(AdoptNS, [values mutableCopy]); + + [mutableKeys.get() addObject:@"kioskmode"]; + [mutableValues.get() addObject:@"true"]; + [self setAttributeKeys:mutableKeys.get() andValues:mutableValues.get()]; + } else +#endif + [self setAttributeKeys:keys andValues:values]; + + if (loadManually) + _mode = NP_FULL; + else + _mode = NP_EMBED; + + _loadManually = loadManually; + _haltable = new WebHaltablePlugin(self); + return self; +} + +- (void)dealloc +{ + ASSERT(!_isStarted); + + [super dealloc]; +} + +- (void)finalize +{ + ASSERT_MAIN_THREAD(); + ASSERT(!_isStarted); + + [super finalize]; +} + +- (WebNetscapePluginPackage *)pluginPackage +{ + return _pluginPackage.get(); +} + +- (BOOL)isFlipped +{ + return YES; +} + +- (NSURL *)URLWithCString:(const char *)URLCString +{ + if (!URLCString) + return nil; + + CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, URLCString, kCFStringEncodingISOLatin1); + ASSERT(string); // All strings should be representable in ISO Latin 1 + + NSString *URLString = [(NSString *)string _web_stringByStrippingReturnCharacters]; + NSURL *URL = [NSURL _web_URLWithDataAsString:URLString relativeToURL:_baseURL.get()]; + CFRelease(string); + if (!URL) + return nil; + + return URL; +} + +- (NSMutableURLRequest *)requestWithURLCString:(const char *)URLCString +{ + NSURL *URL = [self URLWithCString:URLCString]; + if (!URL) + return nil; + + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL]; + Frame* frame = core([self webFrame]); + if (!frame) + return nil; + [request _web_setHTTPReferrer:frame->loader()->outgoingReferrer()]; + return request; +} + +// Methods that subclasses must override +- (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values +{ + ASSERT_NOT_REACHED(); +} + +- (void)handleMouseMoved:(NSEvent *)event +{ + ASSERT_NOT_REACHED(); +} + +- (void)handleMouseEntered:(NSEvent *)event +{ + ASSERT_NOT_REACHED(); +} + +- (void)handleMouseExited:(NSEvent *)event +{ + ASSERT_NOT_REACHED(); +} + +- (void)focusChanged +{ + ASSERT_NOT_REACHED(); +} + +- (void)windowFocusChanged:(BOOL)hasFocus +{ + ASSERT_NOT_REACHED(); +} + +- (BOOL)createPlugin +{ + ASSERT_NOT_REACHED(); + return NO; +} + +- (void)loadStream +{ + ASSERT_NOT_REACHED(); +} + +- (BOOL)shouldStop +{ + ASSERT_NOT_REACHED(); + return YES; +} + +- (void)destroyPlugin +{ + ASSERT_NOT_REACHED(); +} + +- (void)updateAndSetWindow +{ + ASSERT_NOT_REACHED(); +} + +- (void)sendModifierEventWithKeyCode:(int)keyCode character:(char)character +{ + ASSERT_NOT_REACHED(); +} + +- (void)privateBrowsingModeDidChange +{ +} + +- (void)removeTrackingRect +{ + if (_trackingTag) { + [self removeTrackingRect:_trackingTag]; + _trackingTag = 0; + + // Do the following after setting trackingTag to 0 so we don't re-enter. + + // Balance the retain in resetTrackingRect. Use autorelease in case we hold + // the last reference to the window during tear-down, to avoid crashing AppKit. + [[self window] autorelease]; + } +} + +- (void)resetTrackingRect +{ + [self removeTrackingRect]; + if (_isStarted) { + // Retain the window so that removeTrackingRect can work after the window is closed. + [[self window] retain]; + _trackingTag = [self addTrackingRect:[self bounds] owner:self userData:nil assumeInside:NO]; + } +} + +- (void)stopTimers +{ + _shouldFireTimers = NO; +} + +- (void)startTimers +{ + _shouldFireTimers = YES; +} + +- (void)restartTimers +{ + [self stopTimers]; + + if (!_isStarted || [[self window] isMiniaturized]) + return; + + [self startTimers]; +} + +- (NSRect)_windowClipRect +{ + RenderObject* renderer = _element->renderer(); + if (!renderer || !renderer->view()) + return NSZeroRect; + + return toRenderWidget(renderer)->windowClipRect(); +} + +- (NSRect)visibleRect +{ + // WebCore may impose an additional clip (via CSS overflow or clip properties). Fetch + // that clip now. + return NSIntersectionRect([self convertRect:[self _windowClipRect] fromView:nil], [super visibleRect]); +} + +- (void)visibleRectDidChange +{ + [self renewGState]; +} + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (void)sendActivateEvent:(BOOL)activate +{ + if (!_isStarted) + return; + + [self windowFocusChanged:activate]; +} + +- (void)setHasFocus:(BOOL)flag +{ + if (!_isStarted) + return; + + if (_hasFocus == flag) + return; + + _hasFocus = flag; + + [self focusChanged]; +} + +- (void)addWindowObservers +{ + ASSERT([self window]); + + NSWindow *theWindow = [self window]; + + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter addObserver:self selector:@selector(windowWillClose:) + name:NSWindowWillCloseNotification object:theWindow]; + [notificationCenter addObserver:self selector:@selector(windowBecameKey:) + name:NSWindowDidBecomeKeyNotification object:theWindow]; + [notificationCenter addObserver:self selector:@selector(windowResignedKey:) + name:NSWindowDidResignKeyNotification object:theWindow]; + [notificationCenter addObserver:self selector:@selector(windowDidMiniaturize:) + name:NSWindowDidMiniaturizeNotification object:theWindow]; + [notificationCenter addObserver:self selector:@selector(windowDidDeminiaturize:) + name:NSWindowDidDeminiaturizeNotification object:theWindow]; + + [notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchFromUser:) + name:LoginWindowDidSwitchFromUserNotification object:nil]; + [notificationCenter addObserver:self selector:@selector(loginWindowDidSwitchToUser:) + name:LoginWindowDidSwitchToUserNotification object:nil]; +} + +- (void)removeWindowObservers +{ + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter removeObserver:self name:NSWindowWillCloseNotification object:nil]; + [notificationCenter removeObserver:self name:NSWindowDidBecomeKeyNotification object:nil]; + [notificationCenter removeObserver:self name:NSWindowDidResignKeyNotification object:nil]; + [notificationCenter removeObserver:self name:NSWindowDidMiniaturizeNotification object:nil]; + [notificationCenter removeObserver:self name:NSWindowDidDeminiaturizeNotification object:nil]; + [notificationCenter removeObserver:self name:LoginWindowDidSwitchFromUserNotification object:nil]; + [notificationCenter removeObserver:self name:LoginWindowDidSwitchToUserNotification object:nil]; +} + +- (void)start +{ + ASSERT([self currentWindow]); + + if (_isStarted) + return; + + if (_triedAndFailedToCreatePlugin) + return; + + ASSERT([self webView]); + + if (![[[self webView] preferences] arePlugInsEnabled]) + return; + + Frame* frame = core([self webFrame]); + if (!frame) + return; + Page* page = frame->page(); + if (!page) + return; + + bool wasDeferring = page->defersLoading(); + if (!wasDeferring) + page->setDefersLoading(true); + + BOOL result = [self createPlugin]; + + if (!wasDeferring) + page->setDefersLoading(false); + + if (!result) { + _triedAndFailedToCreatePlugin = YES; + return; + } + + _isStarted = YES; + page->didStartPlugin(_haltable.get()); + + [[self webView] addPluginInstanceView:self]; + + if ([self currentWindow]) + [self updateAndSetWindow]; + + if ([self window]) { + [self addWindowObservers]; + if ([[self window] isKeyWindow]) { + [self sendActivateEvent:YES]; + } + [self restartTimers]; + } + + [self resetTrackingRect]; + + [self loadStream]; +} + +- (void)stop +{ + if (![self shouldStop]) + return; + + [self removeTrackingRect]; + + if (!_isStarted) + return; + + if (Frame* frame = core([self webFrame])) { + if (Page* page = frame->page()) + page->didStopPlugin(_haltable.get()); + } + + _isStarted = NO; + + [[self webView] removePluginInstanceView:self]; + + // Stop the timers + [self stopTimers]; + + // Stop notifications and callbacks. + [self removeWindowObservers]; + + [self destroyPlugin]; +} + +- (void)halt +{ + ASSERT(!_isHalted); + ASSERT(_isStarted); + Element *element = [self element]; +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + CGImageRef cgImage = CGImageRetain([core([self webFrame])->nodeImage(element).get() CGImageForProposedRect:nil context:nil hints:nil]); +#else + RetainPtr<CGImageSourceRef> imageRef(AdoptCF, CGImageSourceCreateWithData((CFDataRef)[core([self webFrame])->nodeImage(element).get() TIFFRepresentation], 0)); + CGImageRef cgImage = CGImageSourceCreateImageAtIndex(imageRef.get(), 0, 0); +#endif + ASSERT(cgImage); + + // BitmapImage will release the passed in CGImage on destruction. + RefPtr<Image> nodeImage = BitmapImage::create(cgImage); + ASSERT(element->renderer()); + toRenderWidget(element->renderer())->showSubstituteImage(nodeImage); + [self stop]; + _isHalted = YES; + _hasBeenHalted = YES; +} + +- (void)_clearSubstituteImage +{ + Element* element = [self element]; + if (!element) + return; + + RenderObject* renderer = element->renderer(); + if (!renderer) + return; + + toRenderWidget(renderer)->showSubstituteImage(0); +} + +- (void)resumeFromHalt +{ + ASSERT(_isHalted); + ASSERT(!_isStarted); + [self start]; + + if (_isStarted) + _isHalted = NO; + + ASSERT([self element]->renderer()); + // FIXME 7417484: This is a workaround for plug-ins not drawing immediately. We'd like to detect when the + // plug-in actually draws instead of just assuming it will do so within 0.5 seconds of being restarted. + [self performSelector:@selector(_clearSubstituteImage) withObject:nil afterDelay:ClearSubstituteImageDelay]; +} + +- (BOOL)isHalted +{ + return _isHalted; +} + +- (BOOL)shouldClipOutPlugin +{ + NSWindow *window = [self window]; + return !window || [window isMiniaturized] || [NSApp isHidden] || ![self isDescendantOf:[[self window] contentView]] || [self isHiddenOrHasHiddenAncestor]; +} + +- (BOOL)inFlatteningPaint +{ + RenderObject* renderer = _element->renderer(); + if (renderer && renderer->view()) { + if (FrameView* frameView = renderer->view()->frameView()) + return frameView->paintBehavior() & PaintBehaviorFlattenCompositingLayers; + } + + return NO; +} + +- (BOOL)supportsSnapshotting +{ + return [_pluginPackage.get() supportsSnapshotting]; +} + +- (void)cacheSnapshot +{ + NSImage *snapshot = [[NSImage alloc] initWithSize: [self bounds].size]; + _snapshotting = YES; + [snapshot lockFocus]; + [self drawRect:[self bounds]]; + [snapshot unlockFocus]; + _snapshotting = NO; + + _cachedSnapshot.adoptNS(snapshot); +} + +- (void)clearCachedSnapshot +{ + _cachedSnapshot.clear(); +} + +- (BOOL)hasBeenHalted +{ + return _hasBeenHalted; +} + +- (void)viewWillMoveToWindow:(NSWindow *)newWindow +{ + // We must remove the tracking rect before we move to the new window. + // Once we move to the new window, it will be too late. + [self removeTrackingRect]; + [self removeWindowObservers]; + + // Workaround for: <rdar://problem/3822871> resignFirstResponder is not sent to first responder view when it is removed from the window + [self setHasFocus:NO]; + + if (!newWindow) { + if ([[self webView] hostWindow]) { + // View will be moved out of the actual window but it still has a host window. + [self stopTimers]; + } else { + // View will have no associated windows. + [self stop]; + + // Stop observing WebPreferencesChangedInternalNotification -- we only need to observe this when installed in the view hierarchy. + // When not in the view hierarchy, -viewWillMoveToWindow: and -viewDidMoveToWindow will start/stop the plugin as needed. + [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedInternalNotification object:nil]; + } + } +} + +- (void)viewWillMoveToSuperview:(NSView *)newSuperview +{ + if (!newSuperview) { + // Stop the plug-in when it is removed from its superview. It is not sufficient to do this in -viewWillMoveToWindow:nil, because + // the WebView might still has a hostWindow at that point, which prevents the plug-in from being destroyed. + // There is no need to start the plug-in when moving into a superview. -viewDidMoveToWindow takes care of that. + [self stop]; + + // Stop observing WebPreferencesChangedInternalNotification -- we only need to observe this when installed in the view hierarchy. + // When not in the view hierarchy, -viewWillMoveToWindow: and -viewDidMoveToWindow will start/stop the plugin as needed. + [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedInternalNotification object:nil]; + } +} + +- (void)viewDidMoveToWindow +{ + [self resetTrackingRect]; + + if ([self window]) { + // While in the view hierarchy, observe WebPreferencesChangedInternalNotification so that we can start/stop depending + // on whether plugins are enabled. + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(preferencesHaveChanged:) + name:WebPreferencesChangedInternalNotification + object:nil]; + + _isPrivateBrowsingEnabled = [[[self webView] preferences] privateBrowsingEnabled]; + + // View moved to an actual window. Start it if not already started. + [self start]; + + // Starting the plug-in can result in it removing itself from the window so we need to ensure that we're still in + // place before doing anything that requires a window. + if ([self window]) { + [self restartTimers]; + [self addWindowObservers]; + } + } else if ([[self webView] hostWindow]) { + // View moved out of an actual window, but still has a host window. + // Call setWindow to explicitly "clip out" the plug-in from sight. + // FIXME: It would be nice to do this where we call stopNullEvents in viewWillMoveToWindow. + [self updateAndSetWindow]; + } +} + +- (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow +{ + if (!hostWindow && ![self window]) { + // View will have no associated windows. + [self stop]; + + // Remove WebPreferencesChangedInternalNotification observer -- we will observe once again when we move back into the window + [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedInternalNotification object:nil]; + } +} + +- (void)viewDidMoveToHostWindow +{ + if ([[self webView] hostWindow]) { + // View now has an associated window. Start it if not already started. + [self start]; + } +} + +// MARK: NOTIFICATIONS + +- (void)windowWillClose:(NSNotification *)notification +{ + [self stop]; +} + +- (void)windowBecameKey:(NSNotification *)notification +{ + [self sendActivateEvent:YES]; + [self invalidatePluginContentRect:[self bounds]]; + [self restartTimers]; +} + +- (void)windowResignedKey:(NSNotification *)notification +{ + [self sendActivateEvent:NO]; + [self invalidatePluginContentRect:[self bounds]]; + [self restartTimers]; +} + +- (void)windowDidMiniaturize:(NSNotification *)notification +{ + [self stopTimers]; +} + +- (void)windowDidDeminiaturize:(NSNotification *)notification +{ + [self restartTimers]; +} + +- (void)loginWindowDidSwitchFromUser:(NSNotification *)notification +{ + [self stopTimers]; +} + +-(void)loginWindowDidSwitchToUser:(NSNotification *)notification +{ + [self restartTimers]; +} + +- (void)preferencesHaveChanged:(NSNotification *)notification +{ + WebPreferences *preferences = [[self webView] preferences]; + + if ([notification object] != preferences) + return; + + BOOL arePlugInsEnabled = [preferences arePlugInsEnabled]; + if (_isStarted != arePlugInsEnabled) { + if (arePlugInsEnabled) { + if ([self currentWindow]) { + [self start]; + } + } else { + [self stop]; + [self invalidatePluginContentRect:[self bounds]]; + } + } + + BOOL isPrivateBrowsingEnabled = [preferences privateBrowsingEnabled]; + if (isPrivateBrowsingEnabled != _isPrivateBrowsingEnabled) { + _isPrivateBrowsingEnabled = isPrivateBrowsingEnabled; + [self privateBrowsingModeDidChange]; + } +} + +- (void)renewGState +{ + [super renewGState]; + + // -renewGState is called whenever the view's geometry changes. It's a little hacky to override this method, but + // much safer than walking up the view hierarchy and observing frame/bounds changed notifications, since you don't + // have to track subsequent changes to the view hierarchy and add/remove notification observers. + // NSOpenGLView uses the exact same technique to reshape its OpenGL surface. + + // All of the work this method does may safely be skipped if the view is not in a window. When the view + // is moved back into a window, everything should be set up correctly. + if (![self window]) + return; + + [self updateAndSetWindow]; + + [self resetTrackingRect]; + + // Check to see if the plugin view is completely obscured (scrolled out of view, for example). + // For performance reasons, we send null events at a lower rate to plugins which are obscured. + BOOL oldIsObscured = _isCompletelyObscured; + _isCompletelyObscured = NSIsEmptyRect([self visibleRect]); + if (_isCompletelyObscured != oldIsObscured) + [self restartTimers]; +} + +- (BOOL)becomeFirstResponder +{ + [self setHasFocus:YES]; + return YES; +} + +- (BOOL)resignFirstResponder +{ + [self setHasFocus:NO]; + return YES; +} + +- (WebDataSource *)dataSource +{ + return [[self webFrame] _dataSource]; +} + +- (WebFrame *)webFrame +{ + return kit(_element->document()->frame()); +} + +- (WebView *)webView +{ + return [[self webFrame] webView]; +} + +- (NSWindow *)currentWindow +{ + return [self window] ? [self window] : [[self webView] hostWindow]; +} + +- (WebCore::HTMLPlugInElement*)element +{ + return _element.get(); +} + +- (void)cut:(id)sender +{ + [self sendModifierEventWithKeyCode:7 character:'x']; +} + +- (void)copy:(id)sender +{ + [self sendModifierEventWithKeyCode:8 character:'c']; +} + +- (void)paste:(id)sender +{ + [self sendModifierEventWithKeyCode:9 character:'v']; +} + +- (void)selectAll:(id)sender +{ + [self sendModifierEventWithKeyCode:0 character:'a']; +} + +// AppKit doesn't call mouseDown or mouseUp on right-click. Simulate control-click +// mouseDown and mouseUp so plug-ins get the right-click event as they do in Carbon (3125743). +- (void)rightMouseDown:(NSEvent *)theEvent +{ + [self mouseDown:theEvent]; +} + +- (void)rightMouseUp:(NSEvent *)theEvent +{ + [self mouseUp:theEvent]; +} + + +- (BOOL)convertFromX:(double)sourceX andY:(double)sourceY space:(NPCoordinateSpace)sourceSpace + toX:(double *)destX andY:(double *)destY space:(NPCoordinateSpace)destSpace +{ + // Nothing to do + if (sourceSpace == destSpace) { + if (destX) + *destX = sourceX; + if (destY) + *destY = sourceY; + return YES; + } + + NSPoint sourcePoint = NSMakePoint(sourceX, sourceY); + + NSPoint sourcePointInScreenSpace; + + // First convert to screen space + switch (sourceSpace) { + case NPCoordinateSpacePlugin: + sourcePointInScreenSpace = [self convertPoint:sourcePoint toView:nil]; + sourcePointInScreenSpace = [[self currentWindow] convertBaseToScreen:sourcePointInScreenSpace]; + break; + + case NPCoordinateSpaceWindow: + sourcePointInScreenSpace = [[self currentWindow] convertBaseToScreen:sourcePoint]; + break; + + case NPCoordinateSpaceFlippedWindow: + sourcePoint.y = [[self currentWindow] frame].size.height - sourcePoint.y; + sourcePointInScreenSpace = [[self currentWindow] convertBaseToScreen:sourcePoint]; + break; + + case NPCoordinateSpaceScreen: + sourcePointInScreenSpace = sourcePoint; + break; + + case NPCoordinateSpaceFlippedScreen: + sourcePoint.y = [[[NSScreen screens] objectAtIndex:0] frame].size.height - sourcePoint.y; + sourcePointInScreenSpace = sourcePoint; + break; + default: + return FALSE; + } + + NSPoint destPoint; + + // Then convert back to the destination space + switch (destSpace) { + case NPCoordinateSpacePlugin: + destPoint = [[self currentWindow] convertScreenToBase:sourcePointInScreenSpace]; + destPoint = [self convertPoint:destPoint fromView:nil]; + break; + + case NPCoordinateSpaceWindow: + destPoint = [[self currentWindow] convertScreenToBase:sourcePointInScreenSpace]; + break; + + case NPCoordinateSpaceFlippedWindow: + destPoint = [[self currentWindow] convertScreenToBase:sourcePointInScreenSpace]; + destPoint.y = [[self currentWindow] frame].size.height - destPoint.y; + break; + + case NPCoordinateSpaceScreen: + destPoint = sourcePointInScreenSpace; + break; + + case NPCoordinateSpaceFlippedScreen: + destPoint = sourcePointInScreenSpace; + destPoint.y = [[[NSScreen screens] objectAtIndex:0] frame].size.height - destPoint.y; + break; + + default: + return FALSE; + } + + if (destX) + *destX = destPoint.x; + if (destY) + *destY = destPoint.y; + + return TRUE; +} + + +- (CString)resolvedURLStringForURL:(const char*)url target:(const char*)target +{ + String relativeURLString = String::fromUTF8(url); + if (relativeURLString.isNull()) + return CString(); + + Frame* frame = core([self webFrame]); + if (!frame) + return CString(); + + Frame* targetFrame = frame->tree()->find(String::fromUTF8(target)); + if (!targetFrame) + return CString(); + + if (!frame->document()->securityOrigin()->canAccess(targetFrame->document()->securityOrigin())) + return CString(); + + KURL absoluteURL = targetFrame->loader()->completeURL(relativeURLString); + return absoluteURL.string().utf8(); +} + +- (void)invalidatePluginContentRect:(NSRect)rect +{ + if (RenderBoxModelObject *renderer = toRenderBoxModelObject(_element->renderer())) { + IntRect contentRect(rect); + contentRect.move(renderer->borderLeft() + renderer->paddingLeft(), renderer->borderTop() + renderer->paddingTop()); + + renderer->repaintRectangle(contentRect); + } +} + +- (NSRect)actualVisibleRectInWindow +{ + RenderObject* renderer = _element->renderer(); + if (!renderer || !renderer->view()) + return NSZeroRect; + + FrameView* frameView = renderer->view()->frameView(); + if (!frameView) + return NSZeroRect; + + IntRect widgetRect = renderer->absoluteClippedOverflowRect(); + widgetRect = frameView->contentsToWindow(widgetRect); + return intersection(toRenderWidget(renderer)->windowClipRect(), widgetRect); +} + +#ifndef BUILDING_ON_TIGER +- (CALayer *)pluginLayer +{ + ASSERT_NOT_REACHED(); + return nil; +} +#endif + +@end + +namespace WebKit { + +bool getAuthenticationInfo(const char* protocolStr, const char* hostStr, int32_t port, const char* schemeStr, const char* realmStr, + CString& username, CString& password) +{ + if (strcasecmp(protocolStr, "http") != 0 && + strcasecmp(protocolStr, "https") != 0) + return false; + + NSString *host = [NSString stringWithUTF8String:hostStr]; + if (!hostStr) + return false; + + NSString *protocol = [NSString stringWithUTF8String:protocolStr]; + if (!protocol) + return false; + + NSString *realm = [NSString stringWithUTF8String:realmStr]; + if (!realm) + return NPERR_GENERIC_ERROR; + + NSString *authenticationMethod = NSURLAuthenticationMethodDefault; + if (!strcasecmp(protocolStr, "http")) { + if (!strcasecmp(schemeStr, "basic")) + authenticationMethod = NSURLAuthenticationMethodHTTPBasic; + else if (!strcasecmp(schemeStr, "digest")) + authenticationMethod = NSURLAuthenticationMethodHTTPDigest; + } + + RetainPtr<NSURLProtectionSpace> protectionSpace(AdoptNS, [[NSURLProtectionSpace alloc] initWithHost:host port:port protocol:protocol realm:realm authenticationMethod:authenticationMethod]); + + NSURLCredential *credential = mac(CredentialStorage::get(core(protectionSpace.get()))); + if (!credential) + credential = [[NSURLCredentialStorage sharedCredentialStorage] defaultCredentialForProtectionSpace:protectionSpace.get()]; + if (!credential) + return false; + + if (![credential hasPassword]) + return false; + + username = [[credential user] UTF8String]; + password = [[credential password] UTF8String]; + + return true; +} + +} // namespace WebKit + +#endif // ENABLE(NETSCAPE_PLUGIN_API) + diff --git a/Source/WebKit/mac/Plugins/WebBasePluginPackage.h b/Source/WebKit/mac/Plugins/WebBasePluginPackage.h new file mode 100644 index 0000000..a42a82e --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebBasePluginPackage.h @@ -0,0 +1,95 @@ +/* + * 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 <WebCore/PluginData.h> +#import <wtf/RetainPtr.h> + +#if ENABLE(NETSCAPE_PLUGIN_API) +#import <WebKit/npfunctions.h> +#else +typedef void (*BP_CreatePluginMIMETypesPreferencesFuncPtr)(void); +#endif + +@class WebPluginDatabase; + +@protocol WebPluginManualLoader +- (void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response; +- (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data; +- (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error; +- (void)pluginViewFinishedLoading:(NSView *)pluginView; +@end + +#define WebPluginExtensionsKey @"WebPluginExtensions" +#define WebPluginDescriptionKey @"WebPluginDescription" +#define WebPluginLocalizationNameKey @"WebPluginLocalizationName" +#define WebPluginMIMETypesFilenameKey @"WebPluginMIMETypesFilename" +#define WebPluginMIMETypesKey @"WebPluginMIMETypes" +#define WebPluginNameKey @"WebPluginName" +#define WebPluginTypeDescriptionKey @"WebPluginTypeDescription" +#define WebPluginTypeEnabledKey @"WebPluginTypeEnabled" + +@interface WebBasePluginPackage : NSObject +{ + NSMutableSet *pluginDatabases; + + WTF::String path; + WebCore::PluginInfo pluginInfo; + + RetainPtr<CFBundleRef> cfBundle; + + BP_CreatePluginMIMETypesPreferencesFuncPtr BP_CreatePluginMIMETypesPreferences; +} + ++ (WebBasePluginPackage *)pluginWithPath:(NSString *)pluginPath; +- (id)initWithPath:(NSString *)pluginPath; + +- (BOOL)getPluginInfoFromPLists; + +- (BOOL)load; +- (void)unload; + +- (const WTF::String&)path; + +- (const WebCore::PluginInfo&)pluginInfo; + +- (WTF::String)bundleIdentifier; + +- (BOOL)supportsExtension:(const WTF::String&)extension; +- (BOOL)supportsMIMEType:(const WTF::String&)MIMEType; + +- (NSString *)MIMETypeForExtension:(const WTF::String&)extension; + +- (BOOL)isQuickTimePlugIn; +- (BOOL)isJavaPlugIn; + +- (BOOL)isNativeLibraryData:(NSData *)data; +- (UInt32)versionNumber; +- (void)wasAddedToPluginDatabase:(WebPluginDatabase *)database; +- (void)wasRemovedFromPluginDatabase:(WebPluginDatabase *)database; + +@end diff --git a/Source/WebKit/mac/Plugins/WebBasePluginPackage.mm b/Source/WebKit/mac/Plugins/WebBasePluginPackage.mm new file mode 100644 index 0000000..128834e --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebBasePluginPackage.mm @@ -0,0 +1,481 @@ +/* + * 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/WebBasePluginPackage.h> + +#import <algorithm> +#import <WebCore/WebCoreObjCExtras.h> +#import <WebKit/WebKitNSStringExtras.h> +#import <WebKit/WebNSObjectExtras.h> +#import <WebKit/WebNetscapePluginPackage.h> +#import <WebKit/WebPluginPackage.h> +#import <runtime/InitializeThreading.h> +#import <wtf/Assertions.h> +#import <wtf/Threading.h> +#import <wtf/Vector.h> +#import <wtf/text/CString.h> + +#import <WebKitSystemInterface.h> + +#import "WebKitLogging.h" +#import "WebTypesInternal.h" + +#import <mach-o/arch.h> +#import <mach-o/fat.h> +#import <mach-o/loader.h> + +#define JavaCocoaPluginIdentifier "com.apple.JavaPluginCocoa" +#define JavaCarbonPluginIdentifier "com.apple.JavaAppletPlugin" +#define JavaCFMPluginFilename "Java Applet Plugin Enabler" + +#define QuickTimeCarbonPluginIdentifier "com.apple.QuickTime Plugin.plugin" +#define QuickTimeCocoaPluginIdentifier "com.apple.quicktime.webplugin" + +@interface NSArray (WebPluginExtensions) +- (NSArray *)_web_lowercaseStrings; +@end; + +using namespace std; +using namespace WebCore; + +@implementation WebBasePluginPackage + ++ (void)initialize +{ + JSC::initializeThreading(); + WTF::initializeMainThreadToProcessMainThread(); +#ifndef BUILDING_ON_TIGER + WebCoreObjCFinalizeOnMainThread(self); +#endif +} + ++ (WebBasePluginPackage *)pluginWithPath:(NSString *)pluginPath +{ + + WebBasePluginPackage *pluginPackage = [[WebPluginPackage alloc] initWithPath:pluginPath]; + + if (!pluginPackage) { +#if ENABLE(NETSCAPE_PLUGIN_API) + pluginPackage = [[WebNetscapePluginPackage alloc] initWithPath:pluginPath]; +#else + return nil; +#endif + } + + return [pluginPackage autorelease]; +} + ++ (NSString *)preferredLocalizationName +{ + return WebCFAutorelease(WKCopyCFLocalizationPreferredName(NULL)); +} + +static NSString *pathByResolvingSymlinksAndAliases(NSString *thePath) +{ + NSString *newPath = [thePath stringByResolvingSymlinksInPath]; + + FSRef fref; + OSStatus err; + + err = FSPathMakeRef((const UInt8 *)[thePath fileSystemRepresentation], &fref, NULL); + if (err != noErr) + return newPath; + + Boolean targetIsFolder; + Boolean wasAliased; + err = FSResolveAliasFileWithMountFlags(&fref, TRUE, &targetIsFolder, &wasAliased, kResolveAliasFileNoUI); + if (err != noErr) + return newPath; + + if (wasAliased) { + CFURLRef URL = CFURLCreateFromFSRef(kCFAllocatorDefault, &fref); + newPath = [(NSURL *)URL path]; + CFRelease(URL); + } + + return newPath; +} + +- (id)initWithPath:(NSString *)pluginPath +{ + if (!(self = [super init])) + return nil; + + path = pathByResolvingSymlinksAndAliases(pluginPath); + cfBundle.adoptCF(CFBundleCreate(kCFAllocatorDefault, (CFURLRef)[NSURL fileURLWithPath:path])); + +#ifndef __ppc__ + // 32-bit PowerPC is the only platform where non-bundled CFM plugins are supported + if (!cfBundle) { + [self release]; + return nil; + } +#endif + + return self; +} + +- (void)unload +{ +} + +- (void)createPropertyListFile +{ + if ([self load] && BP_CreatePluginMIMETypesPreferences) { + BP_CreatePluginMIMETypesPreferences(); + [self unload]; + } +} + +- (NSDictionary *)pListForPath:(NSString *)pListPath createFile:(BOOL)createFile +{ + if (createFile) + [self createPropertyListFile]; + + NSDictionary *pList = nil; + NSData *data = [NSData dataWithContentsOfFile:pListPath]; + if (data) { + pList = [NSPropertyListSerialization propertyListFromData:data + mutabilityOption:NSPropertyListImmutable + format:nil + errorDescription:nil]; + } + + return pList; +} + +- (id)_objectForInfoDictionaryKey:(NSString *)key +{ + CFDictionaryRef bundleInfoDictionary = CFBundleGetInfoDictionary(cfBundle.get()); + if (!bundleInfoDictionary) + return nil; + + return (id)CFDictionaryGetValue(bundleInfoDictionary, key); +} + +- (BOOL)getPluginInfoFromPLists +{ + if (!cfBundle) + return NO; + + NSDictionary *MIMETypes = nil; + NSString *pListFilename = [self _objectForInfoDictionaryKey:WebPluginMIMETypesFilenameKey]; + + // Check if the MIME types are claimed in a plist in the user's preferences directory. + if (pListFilename) { + NSString *pListPath = [NSString stringWithFormat:@"%@/Library/Preferences/%@", NSHomeDirectory(), pListFilename]; + NSDictionary *pList = [self pListForPath:pListPath createFile:NO]; + if (pList) { + // If the plist isn't localized, have the plug-in recreate it in the preferred language. + NSString *localizationName = [pList objectForKey:WebPluginLocalizationNameKey]; + if (![localizationName isEqualToString:[[self class] preferredLocalizationName]]) + pList = [self pListForPath:pListPath createFile:YES]; + MIMETypes = [pList objectForKey:WebPluginMIMETypesKey]; + } else + // Plist doesn't exist, ask the plug-in to create it. + MIMETypes = [[self pListForPath:pListPath createFile:YES] objectForKey:WebPluginMIMETypesKey]; + } + + if (!MIMETypes) { + MIMETypes = [self _objectForInfoDictionaryKey:WebPluginMIMETypesKey]; + if (!MIMETypes) + return NO; + } + + NSEnumerator *keyEnumerator = [MIMETypes keyEnumerator]; + NSDictionary *MIMEDictionary; + NSString *MIME, *description; + NSArray *extensions; + + while ((MIME = [keyEnumerator nextObject]) != nil) { + MIMEDictionary = [MIMETypes objectForKey:MIME]; + + // FIXME: Consider storing disabled MIME types. + NSNumber *isEnabled = [MIMEDictionary objectForKey:WebPluginTypeEnabledKey]; + if (isEnabled && [isEnabled boolValue] == NO) + continue; + + MimeClassInfo mimeClassInfo; + + extensions = [[MIMEDictionary objectForKey:WebPluginExtensionsKey] _web_lowercaseStrings]; + for (NSUInteger i = 0; i < [extensions count]; ++i) { + // The DivX plug-in lists multiple extensions in a comma separated string instead of using + // multiple array elements in the property list. Work around this here by splitting the + // extension string into components. + NSArray *extensionComponents = [[extensions objectAtIndex:i] componentsSeparatedByString:@","]; + + for (NSString *extension in extensionComponents) + mimeClassInfo.extensions.append(extension); + } + + if ([extensions count] == 0) + extensions = [NSArray arrayWithObject:@""]; + + mimeClassInfo.type = String(MIME).lower(); + + description = [MIMEDictionary objectForKey:WebPluginTypeDescriptionKey]; + mimeClassInfo.desc = description; + + pluginInfo.mimes.append(mimeClassInfo); + if (!description) + description = @""; + } + + NSString *filename = [(NSString *)path lastPathComponent]; + pluginInfo.file = filename; + + NSString *theName = [self _objectForInfoDictionaryKey:WebPluginNameKey]; + if (!theName) + theName = filename; + pluginInfo.name = theName; + + description = [self _objectForInfoDictionaryKey:WebPluginDescriptionKey]; + if (!description) + description = filename; + pluginInfo.desc = description; + + return YES; +} + +- (BOOL)load +{ + if (cfBundle && !BP_CreatePluginMIMETypesPreferences) + BP_CreatePluginMIMETypesPreferences = (BP_CreatePluginMIMETypesPreferencesFuncPtr)CFBundleGetFunctionPointerForName(cfBundle.get(), CFSTR("BP_CreatePluginMIMETypesPreferences")); + + return YES; +} + +- (void)dealloc +{ + ASSERT(!pluginDatabases || [pluginDatabases count] == 0); + [pluginDatabases release]; + + [super dealloc]; +} + +- (void)finalize +{ + ASSERT_MAIN_THREAD(); + ASSERT(!pluginDatabases || [pluginDatabases count] == 0); + [pluginDatabases release]; + + [super finalize]; +} + +- (const String&)path +{ + return path; +} + +- (const PluginInfo&)pluginInfo +{ + return pluginInfo; +} + +- (BOOL)supportsExtension:(const String&)extension +{ + ASSERT(extension.lower() == extension); + + for (size_t i = 0; i < pluginInfo.mimes.size(); ++i) { + const Vector<String>& extensions = pluginInfo.mimes[i].extensions; + + if (find(extensions.begin(), extensions.end(), extension) != extensions.end()) + return YES; + } + + return NO; +} + +- (BOOL)supportsMIMEType:(const WTF::String&)mimeType +{ + ASSERT(mimeType.lower() == mimeType); + + for (size_t i = 0; i < pluginInfo.mimes.size(); ++i) { + if (pluginInfo.mimes[i].type == mimeType) + return YES; + } + + return NO; +} + +- (NSString *)MIMETypeForExtension:(const String&)extension +{ + ASSERT(extension.lower() == extension); + + for (size_t i = 0; i < pluginInfo.mimes.size(); ++i) { + const MimeClassInfo& mimeClassInfo = pluginInfo.mimes[i]; + const Vector<String>& extensions = mimeClassInfo.extensions; + + if (find(extensions.begin(), extensions.end(), extension) != extensions.end()) + return mimeClassInfo.type; + } + + return nil; +} + +- (BOOL)isQuickTimePlugIn +{ + const String& bundleIdentifier = [self bundleIdentifier]; + return bundleIdentifier == QuickTimeCocoaPluginIdentifier || bundleIdentifier == QuickTimeCocoaPluginIdentifier; +} + +- (BOOL)isJavaPlugIn +{ + const String& bundleIdentifier = [self bundleIdentifier]; + return bundleIdentifier == JavaCocoaPluginIdentifier || bundleIdentifier == JavaCarbonPluginIdentifier || + equalIgnoringCase(pluginInfo.file, JavaCFMPluginFilename); +} + +static inline void swapIntsInHeader(uint32_t* rawData, size_t length) +{ + for (size_t i = 0; i < length; ++i) + rawData[i] = OSSwapInt32(rawData[i]); +} + +- (BOOL)isNativeLibraryData:(NSData *)data +{ + NSUInteger sizeInBytes = [data length]; + Vector<uint32_t, 128> rawData((sizeInBytes + 3) / 4); + memcpy(rawData.data(), [data bytes], sizeInBytes); + + unsigned numArchs = 0; + struct fat_arch singleArch = { 0, 0, 0, 0, 0 }; + struct fat_arch* archs = 0; + + if (sizeInBytes >= sizeof(struct mach_header_64)) { + uint32_t magic = *rawData.data(); + + if (magic == MH_MAGIC || magic == MH_CIGAM) { + // We have a 32-bit thin binary + struct mach_header* header = (struct mach_header*)rawData.data(); + + // Check if we need to swap the bytes + if (magic == MH_CIGAM) + swapIntsInHeader(rawData.data(), rawData.size()); + + singleArch.cputype = header->cputype; + singleArch.cpusubtype = header->cpusubtype; + + archs = &singleArch; + numArchs = 1; + } else if (magic == MH_MAGIC_64 || magic == MH_CIGAM_64) { + // We have a 64-bit thin binary + struct mach_header_64* header = (struct mach_header_64*)rawData.data(); + + // Check if we need to swap the bytes + if (magic == MH_CIGAM_64) + swapIntsInHeader(rawData.data(), rawData.size()); + + singleArch.cputype = header->cputype; + singleArch.cpusubtype = header->cpusubtype; + + archs = &singleArch; + numArchs = 1; + } else if (magic == FAT_MAGIC || magic == FAT_CIGAM) { + // We have a fat (universal) binary + + // Check if we need to swap the bytes + if (magic == FAT_CIGAM) + swapIntsInHeader(rawData.data(), rawData.size()); + + COMPILE_ASSERT(sizeof(struct fat_header) % sizeof(uint32_t) == 0, struct_fat_header_must_be_integral_size_of_uint32_t); + archs = reinterpret_cast<struct fat_arch*>(rawData.data() + sizeof(struct fat_header) / sizeof(uint32_t)); + numArchs = reinterpret_cast<struct fat_header*>(rawData.data())->nfat_arch; + + unsigned maxArchs = (sizeInBytes - sizeof(struct fat_header)) / sizeof(struct fat_arch); + if (numArchs > maxArchs) + numArchs = maxArchs; + } + } + + if (!archs || !numArchs) + return NO; + + const NXArchInfo* localArch = NXGetLocalArchInfo(); + if (!localArch) + return NO; + + cpu_type_t cputype = localArch->cputype; + cpu_subtype_t cpusubtype = localArch->cpusubtype; + +#ifdef __x86_64__ + // NXGetLocalArchInfo returns CPU_TYPE_X86 even when running in 64-bit. + // See <rdar://problem/4996965> for more information. + cputype = CPU_TYPE_X86_64; +#endif + + return NXFindBestFatArch(cputype, cpusubtype, archs, numArchs) != 0; +} + +- (UInt32)versionNumber +{ + // CFBundleGetVersionNumber doesn't work with all possible versioning schemes, but we think for now it's good enough for us. + return CFBundleGetVersionNumber(cfBundle.get()); +} + +- (void)wasAddedToPluginDatabase:(WebPluginDatabase *)database +{ + if (!pluginDatabases) + pluginDatabases = [[NSMutableSet alloc] init]; + + ASSERT(![pluginDatabases containsObject:database]); + [pluginDatabases addObject:database]; +} + +- (void)wasRemovedFromPluginDatabase:(WebPluginDatabase *)database +{ + ASSERT(pluginDatabases); + ASSERT([pluginDatabases containsObject:database]); + + [pluginDatabases removeObject:database]; +} + +- (WTF::String)bundleIdentifier +{ + return CFBundleGetIdentifier(cfBundle.get()); +} + +@end + +@implementation NSArray (WebPluginExtensions) + +- (NSArray *)_web_lowercaseStrings +{ + NSMutableArray *lowercaseStrings = [NSMutableArray arrayWithCapacity:[self count]]; + NSEnumerator *strings = [self objectEnumerator]; + NSString *string; + + while ((string = [strings nextObject]) != nil) { + if ([string isKindOfClass:[NSString class]]) + [lowercaseStrings addObject:[string lowercaseString]]; + } + + return lowercaseStrings; +} + +@end diff --git a/Source/WebKit/mac/Plugins/WebJavaPlugIn.h b/Source/WebKit/mac/Plugins/WebJavaPlugIn.h new file mode 100644 index 0000000..5a1be6d --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebJavaPlugIn.h @@ -0,0 +1,85 @@ +/* + * 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. + * 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 <JavaVM/jni.h> + +/*! + The Java plug-in adds the following additional methods to facilitate JNI + access to Java VM via the plug-in. +*/ + +typedef enum { + WebJNIReturnTypeInvalid = 0, + WebJNIReturnTypeVoid, + WebJNIReturnTypeObject, + WebJNIReturnTypeBoolean, + WebJNIReturnTypeByte, + WebJNIReturnTypeChar, + WebJNIReturnTypeShort, + WebJNIReturnTypeInt, + WebJNIReturnTypeLong, + WebJNIReturnTypeFloat, + WebJNIReturnTypeDouble +} WebJNIReturnType; + +@interface NSObject (WebJavaPlugIn) + +/*! + @method webPlugInGetApplet + @discusssion This returns the jobject representing the java applet to the + WebPlugInContainer. It should always be called from the AppKit Main Thread. + This method is only implemented by the Java plug-in. +*/ +- (jobject)webPlugInGetApplet; + +/*! + @method webPlugInCallJava:isStatic:returnType:method:arguments:callingURL:exceptionDescription: + @param object The Java instance that will receive the method call. + @param isStatic A flag that indicated whether the method is a class method. + @param returnType The return type of the Java method. + @param method The ID of the Java method to call. + @param args The arguments to use with the method invocation. + @param callingURL The URL of the page that contains the JavaScript that is calling Java. + @param exceptionDescription Pass in nil or the address of pointer to a string object. If any exception + is thrown by Java the return value will be a description of the exception, otherwise nil. + @discussion Calls to Java from native code should not make direct + use of JNI. Instead they should use this method to dispatch calls to the + Java VM. This is required to guarantee that the correct thread will receive + the call. webPlugInCallJava:isStatic:returnType:method:arguments:callingURL:exceptionDescription: must + always be called from the AppKit main thread. This method is only implemented by the Java plug-in. + @result The result of the method invocation. +*/ +- (jvalue)webPlugInCallJava:(jobject)object + isStatic:(BOOL)isStatic + returnType:(WebJNIReturnType)returnType + method:(jmethodID)method + arguments:(jvalue*)args + callingURL:(NSURL *)url + exceptionDescription:(NSString **)exceptionString; + +@end diff --git a/Source/WebKit/mac/Plugins/WebNetscapeContainerCheckContextInfo.h b/Source/WebKit/mac/Plugins/WebNetscapeContainerCheckContextInfo.h new file mode 100644 index 0000000..dcd4dd4 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebNetscapeContainerCheckContextInfo.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. + * 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 "WebBaseNetscapePluginView.h" + +#if ENABLE(NETSCAPE_PLUGIN_API) + +@interface WebNetscapeContainerCheckContextInfo : NSObject { + uint32_t _checkRequestID; + void (*_callback)(NPP npp, uint32_t, NPBool, void *); + void *_context; +} + +- (id)initWithCheckRequestID:(uint32_t)checkRequestID callbackFunc:(void (*)(NPP npp, uint32_t checkID, NPBool allowed, void* context))callbackFunc context:(void*)context; +- (uint32_t)checkRequestID; +- (void (*)(NPP npp, uint32_t, NPBool, void*))callback; +- (void*)context; + +@end + +#endif // ENABLE(NETSCAPE_PLUGIN_API) diff --git a/Source/WebKit/mac/Plugins/WebNetscapeContainerCheckContextInfo.mm b/Source/WebKit/mac/Plugins/WebNetscapeContainerCheckContextInfo.mm new file mode 100644 index 0000000..8991c95 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebNetscapeContainerCheckContextInfo.mm @@ -0,0 +1,61 @@ +/* + * 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 "WebNetscapeContainerCheckContextInfo.h" + +#if ENABLE(NETSCAPE_PLUGIN_API) + +@implementation WebNetscapeContainerCheckContextInfo + +- (id)initWithCheckRequestID:(uint32_t)checkRequestID callbackFunc:(void (*)(NPP npp, uint32_t checkID, NPBool allowed, void* context))callbackFunc context:(void*)context +{ + self = [super init]; + if (!self) + return nil; + + _checkRequestID = checkRequestID; + _callback = callbackFunc; + _context = context; + return self; +} + +- (uint32_t)checkRequestID +{ + return _checkRequestID; +} + +- (void (*)(NPP npp, uint32_t, NPBool, void*))callback +{ + return _callback; +} + +- (void*)context +{ + return _context; +} + +@end + +#endif // ENABLE(NETSCAPE_PLUGIN_API) diff --git a/Source/WebKit/mac/Plugins/WebNetscapeContainerCheckPrivate.h b/Source/WebKit/mac/Plugins/WebNetscapeContainerCheckPrivate.h new file mode 100644 index 0000000..799680b --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebNetscapeContainerCheckPrivate.h @@ -0,0 +1,64 @@ +/* + * 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. + * 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 WebNetscapeContainerCheckPrivate_h +#define WebNetscapeContainerCheckPrivate_h + +#include <WebKit/npapi.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define WKNVBrowserContainerCheckFuncs 1701 +#define WKNVBrowserContainerCheckFuncsVersion 2 + +#define WKNVBrowserContainerCheckFuncsVersionHasGetLocation 2 + +typedef uint32_t (*WKN_CheckIfAllowedToLoadURLProcPtr)(NPP npp, const char* url, const char* frame, void (*callbackFunc)(NPP npp, uint32_t, NPBool allowed, void* context), void* context); +typedef void (*WKN_CancelCheckIfAllowedToLoadURLProcPtr)(NPP npp, uint32_t); +typedef char* (*WKN_ResolveURLProcPtr)(NPP npp, const char* url, const char* target); + +uint32_t WKN_CheckIfAllowedToLoadURL(NPP npp, const char* url, const char* frame, void (*callbackFunc)(NPP npp, uint32_t, NPBool allowed, void* context), void* context); +void WKN_CancelCheckIfAllowedToLoadURL(NPP npp, uint32_t); +char* WKN_ResolveURL(NPP npp, const char* url, const char* target); + +typedef struct _WKNBrowserContainerCheckFuncs { + uint16_t size; + uint16_t version; + + WKN_CheckIfAllowedToLoadURLProcPtr checkIfAllowedToLoadURL; + WKN_CancelCheckIfAllowedToLoadURLProcPtr cancelCheckIfAllowedToLoadURL; + WKN_ResolveURLProcPtr resolveURL; +} WKNBrowserContainerCheckFuncs; + +#ifdef __cplusplus +} +#endif + +#endif // WebNetscapeContainerCheckPrivate_h diff --git a/Source/WebKit/mac/Plugins/WebNetscapeContainerCheckPrivate.mm b/Source/WebKit/mac/Plugins/WebNetscapeContainerCheckPrivate.mm new file mode 100644 index 0000000..d326b56 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebNetscapeContainerCheckPrivate.mm @@ -0,0 +1,48 @@ +/* + * 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. + * 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 "WebNetscapeContainerCheckPrivate.h" + +#import "WebNetscapePluginView.h" + +#if ENABLE(NETSCAPE_PLUGIN_API) + +WKNBrowserContainerCheckFuncs *browserContainerCheckFuncs() +{ + static WKNBrowserContainerCheckFuncs funcs = { + sizeof(WKNBrowserContainerCheckFuncs), + WKNVBrowserContainerCheckFuncsVersion, + WKN_CheckIfAllowedToLoadURL, + WKN_CancelCheckIfAllowedToLoadURL, + WKN_ResolveURL + }; + + return &funcs; +} + +#endif // ENABLE(NETSCAPE_PLUGIN_API) diff --git a/Source/WebKit/mac/Plugins/WebNetscapeDeprecatedFunctions.c b/Source/WebKit/mac/Plugins/WebNetscapeDeprecatedFunctions.c new file mode 100644 index 0000000..797e824 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebNetscapeDeprecatedFunctions.c @@ -0,0 +1,50 @@ +/* + * 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. + * + * 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. + */ + +#include "WebNetscapeDeprecatedFunctions.h" + +#if ENABLE(NETSCAPE_PLUGIN_API) && !defined(__LP64__) + +OSErr WebGetDiskFragment(const FSSpec *fileSpec, UInt32 offset, UInt32 length, ConstStr63Param fragName, CFragLoadOptions options, CFragConnectionID *connID, Ptr *mainAddr, Str255 errMessage) +{ + return GetDiskFragment(fileSpec, offset, length, fragName, options, connID, mainAddr, errMessage); +} + +OSErr WebCloseConnection(CFragConnectionID *connID) +{ + return CloseConnection(connID); +} + +SInt16 WebLMGetCurApRefNum(void) +{ + return LMGetCurApRefNum(); +} + +extern void WebLMSetCurApRefNum(SInt16 value) +{ + LMSetCurApRefNum(value); +} + +#endif /* ENABLE(NETSCAPE_PLUGIN_API) && !defined(__LP64__) */ diff --git a/Source/WebKit/mac/Plugins/WebNetscapeDeprecatedFunctions.h b/Source/WebKit/mac/Plugins/WebNetscapeDeprecatedFunctions.h new file mode 100644 index 0000000..dc8bf4d --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebNetscapeDeprecatedFunctions.h @@ -0,0 +1,43 @@ +/* + * 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. + * + * 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. + */ + +#if ENABLE(NETSCAPE_PLUGIN_API) && !defined(__LP64__) + +#import <CoreServices/CoreServices.h> + +#ifdef __cplusplus +extern "C" { +#endif + +extern OSErr WebGetDiskFragment(const FSSpec *fileSpec, UInt32 offset, UInt32 length, ConstStr63Param fragName, CFragLoadOptions options, CFragConnectionID *connID, Ptr *mainAddr, Str255 errMessage); +extern OSErr WebCloseConnection(CFragConnectionID *connID); +extern SInt16 WebLMGetCurApRefNum(void); +extern void WebLMSetCurApRefNum(SInt16 value); + +#ifdef __cplusplus +} +#endif + +#endif /* ENABLE(NETSCAPE_PLUGIN_API) && !defined(__LP64__) */ diff --git a/Source/WebKit/mac/Plugins/WebNetscapePluginEventHandler.h b/Source/WebKit/mac/Plugins/WebNetscapePluginEventHandler.h new file mode 100644 index 0000000..b9bdf7d --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebNetscapePluginEventHandler.h @@ -0,0 +1,83 @@ +/* + * 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. + * + * 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. + */ + +#ifndef WebNetscapePluginEventHandler_h +#define WebNetscapePluginEventHandler_h + +#import "WebNetscapePluginView.h" + +#if ENABLE(NETSCAPE_PLUGIN_API) + +@class NSEvent; +@class WebNetscapePluginView; + +struct CGRect; + +class WebNetscapePluginEventHandler { +public: + static WebNetscapePluginEventHandler* create(WebNetscapePluginView*); + virtual ~WebNetscapePluginEventHandler() { } + + virtual void drawRect(CGContextRef, const NSRect&) = 0; + + virtual void mouseDown(NSEvent*) = 0; + virtual void mouseDragged(NSEvent*) = 0; + virtual void mouseEntered(NSEvent*) = 0; + virtual void mouseExited(NSEvent*) = 0; + virtual void mouseMoved(NSEvent*) = 0; + virtual void mouseUp(NSEvent*) = 0; + virtual bool scrollWheel(NSEvent*) = 0; + + virtual void keyDown(NSEvent*) = 0; + virtual void keyUp(NSEvent*) = 0; + virtual void flagsChanged(NSEvent*) = 0; + virtual void syntheticKeyDownWithCommandModifier(int keyCode, char character) = 0; + + virtual void focusChanged(bool hasFocus) = 0; + virtual void windowFocusChanged(bool hasFocus) = 0; + + virtual void startTimers(bool throttleTimers) { } + virtual void stopTimers() { } + + // Returns the platform specific window used in NPP_SetWindow + virtual void* platformWindow(NSWindow*) = 0; + + bool currentEventIsUserGesture() const { return m_currentEventIsUserGesture; } +protected: + WebNetscapePluginEventHandler(WebNetscapePluginView* pluginView) + : m_pluginView(pluginView) + , m_currentEventIsUserGesture(false) + { + } + + WebNetscapePluginView* m_pluginView; + bool m_currentEventIsUserGesture; +}; + +#endif // ENABLE(NETSCAPE_PLUGIN_API) + +#endif // WebNetscapePluginEventHandler_h + + diff --git a/Source/WebKit/mac/Plugins/WebNetscapePluginEventHandler.mm b/Source/WebKit/mac/Plugins/WebNetscapePluginEventHandler.mm new file mode 100644 index 0000000..c886d7b --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebNetscapePluginEventHandler.mm @@ -0,0 +1,50 @@ +/* + * 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. + * + * 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. + */ + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#import "WebNetscapePluginEventHandler.h" + +#import <wtf/Assertions.h> +#import "WebNetscapePluginView.h" +#import "WebNetscapePluginEventHandlerCarbon.h" +#import "WebNetscapePluginEventHandlerCocoa.h" + +WebNetscapePluginEventHandler* WebNetscapePluginEventHandler::create(WebNetscapePluginView* pluginView) +{ + switch ([pluginView eventModel]) { +#ifndef NP_NO_CARBON + case NPEventModelCarbon: + return new WebNetscapePluginEventHandlerCarbon(pluginView); +#endif // NP_NO_CARBON + case NPEventModelCocoa: + return new WebNetscapePluginEventHandlerCocoa(pluginView); + default: + ASSERT_NOT_REACHED(); + return 0; + } +} + +#endif // ENABLE(NETSCAPE_PLUGIN_API) diff --git a/Source/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCarbon.h b/Source/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCarbon.h new file mode 100644 index 0000000..676fd17 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCarbon.h @@ -0,0 +1,82 @@ +/* + * 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. + * + * 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. + */ + +#if ENABLE(NETSCAPE_PLUGIN_API) && !defined(__LP64__) + +#ifndef WebNetscapePluginEventHandlerCarbon_h +#define WebNetscapePluginEventHandlerCarbon_h + +#include "WebNetscapePluginEventHandler.h" + +#import <Carbon/Carbon.h> +#import <wtf/RetainPtr.h> + +class WebNetscapePluginEventHandlerCarbon : public WebNetscapePluginEventHandler { +public: + WebNetscapePluginEventHandlerCarbon(WebNetscapePluginView*); + + virtual void drawRect(CGContextRef, const NSRect&); + + virtual void mouseDown(NSEvent*); + virtual void mouseDragged(NSEvent*); + virtual void mouseEntered(NSEvent*); + virtual void mouseExited(NSEvent*); + virtual void mouseMoved(NSEvent*); + virtual void mouseUp(NSEvent*); + virtual bool scrollWheel(NSEvent*); + + virtual void keyDown(NSEvent*); + virtual void keyUp(NSEvent*); + virtual void flagsChanged(NSEvent*); + virtual void syntheticKeyDownWithCommandModifier(int keyCode, char character); + + virtual void windowFocusChanged(bool hasFocus); + virtual void focusChanged(bool hasFocus); + + virtual void startTimers(bool throttleTimers); + virtual void stopTimers(); + + virtual void* platformWindow(NSWindow*); + +private: + void sendNullEvent(); + + void installKeyEventHandler(); + void removeKeyEventHandler(); + + static OSStatus TSMEventHandler(EventHandlerCallRef, EventRef, void *eventHandler); + static void nullEventTimerFired(CFRunLoopTimerRef, void *context); + + bool sendEvent(EventRecord*); + + EventHandlerRef m_keyEventHandler; + bool m_suspendKeyUpEvents; + RetainPtr<CFRunLoopTimerRef> m_nullEventTimer; +}; + +#endif // WebNetscapePluginEventHandlerCarbon_h + +#endif // ENABLE(NETSCAPE_PLUGIN_API) && !defined(__LP64__) + diff --git a/Source/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCarbon.mm b/Source/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCarbon.mm new file mode 100644 index 0000000..a3aca1f --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCarbon.mm @@ -0,0 +1,429 @@ +/* + * 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. + * + * 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. + */ + +#if ENABLE(NETSCAPE_PLUGIN_API) && !defined(__LP64__) + +#import "WebNetscapePluginEventHandlerCarbon.h" + +#import "WebNetscapePluginView.h" +#import "WebKitLogging.h" +#import "WebKitSystemInterface.h" + +// Send null events 50 times a second when active, so plug-ins like Flash get high frame rates. +#define NullEventIntervalActive 0.02 +#define NullEventIntervalNotActive 0.25 + +WebNetscapePluginEventHandlerCarbon::WebNetscapePluginEventHandlerCarbon(WebNetscapePluginView* pluginView) + : WebNetscapePluginEventHandler(pluginView) + , m_keyEventHandler(0) + , m_suspendKeyUpEvents(false) +{ +} + +static void getCarbonEvent(EventRecord* carbonEvent) +{ + carbonEvent->what = nullEvent; + carbonEvent->message = 0; + carbonEvent->when = TickCount(); + + GetGlobalMouse(&carbonEvent->where); + carbonEvent->modifiers = GetCurrentKeyModifiers(); + if (!Button()) + carbonEvent->modifiers |= btnState; +} + +static EventModifiers modifiersForEvent(NSEvent *event) +{ + EventModifiers modifiers; + unsigned int modifierFlags = [event modifierFlags]; + NSEventType eventType = [event type]; + + modifiers = 0; + + if (eventType != NSLeftMouseDown && eventType != NSRightMouseDown) + modifiers |= btnState; + + if (modifierFlags & NSCommandKeyMask) + modifiers |= cmdKey; + + if (modifierFlags & NSShiftKeyMask) + modifiers |= shiftKey; + + if (modifierFlags & NSAlphaShiftKeyMask) + modifiers |= alphaLock; + + if (modifierFlags & NSAlternateKeyMask) + modifiers |= optionKey; + + if (modifierFlags & NSControlKeyMask || eventType == NSRightMouseDown) + modifiers |= controlKey; + + return modifiers; +} + +static void getCarbonEvent(EventRecord *carbonEvent, NSEvent *cocoaEvent) +{ + if (WKConvertNSEventToCarbonEvent(carbonEvent, cocoaEvent)) + return; + + NSPoint where = [[cocoaEvent window] convertBaseToScreen:[cocoaEvent locationInWindow]]; + + carbonEvent->what = nullEvent; + carbonEvent->message = 0; + carbonEvent->when = (UInt32)([cocoaEvent timestamp] * 60); // seconds to ticks + carbonEvent->where.h = (short)where.x; + carbonEvent->where.v = (short)(NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - where.y); + carbonEvent->modifiers = modifiersForEvent(cocoaEvent); +} + +void WebNetscapePluginEventHandlerCarbon::sendNullEvent() +{ + EventRecord event; + + getCarbonEvent(&event); + + // Plug-in should not react to cursor position when not active or when a menu is down. + MenuTrackingData trackingData; + OSStatus error = GetMenuTrackingData(NULL, &trackingData); + + // Plug-in should not react to cursor position when the actual window is not key. + if (![[m_pluginView window] isKeyWindow] || (error == noErr && trackingData.menu)) { + // FIXME: Does passing a v and h of -1 really prevent it from reacting to the cursor position? + event.where.v = -1; + event.where.h = -1; + } + + sendEvent(&event); +} + +void WebNetscapePluginEventHandlerCarbon::drawRect(CGContextRef, const NSRect&) +{ + EventRecord event; + + getCarbonEvent(&event); + event.what = updateEvt; + WindowRef windowRef = (WindowRef)[[m_pluginView window] windowRef]; + event.message = (unsigned long)windowRef; + + BOOL acceptedEvent; + acceptedEvent = sendEvent(&event); + + LOG(PluginEvents, "NPP_HandleEvent(updateEvt): %d", acceptedEvent); +} + +void WebNetscapePluginEventHandlerCarbon::mouseDown(NSEvent* theEvent) +{ + EventRecord event; + + getCarbonEvent(&event, theEvent); + event.what = ::mouseDown; + + BOOL acceptedEvent; + acceptedEvent = sendEvent(&event); + + LOG(PluginEvents, "NPP_HandleEvent(mouseDown): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h); +} + +void WebNetscapePluginEventHandlerCarbon::mouseUp(NSEvent* theEvent) +{ + EventRecord event; + + getCarbonEvent(&event, theEvent); + event.what = ::mouseUp; + + BOOL acceptedEvent; + acceptedEvent = sendEvent(&event); + + LOG(PluginEvents, "NPP_HandleEvent(mouseUp): %d pt.v=%d, pt.h=%d", acceptedEvent, event.where.v, event.where.h); +} + +bool WebNetscapePluginEventHandlerCarbon::scrollWheel(NSEvent* theEvent) +{ + return false; +} + +void WebNetscapePluginEventHandlerCarbon::mouseEntered(NSEvent* theEvent) +{ + EventRecord event; + + getCarbonEvent(&event, theEvent); + event.what = NPEventType_AdjustCursorEvent; + + BOOL acceptedEvent; + acceptedEvent = sendEvent(&event); + + LOG(PluginEvents, "NPP_HandleEvent(mouseEntered): %d", acceptedEvent); +} + +void WebNetscapePluginEventHandlerCarbon::mouseExited(NSEvent* theEvent) +{ + EventRecord event; + + getCarbonEvent(&event, theEvent); + event.what = NPEventType_AdjustCursorEvent; + + BOOL acceptedEvent; + acceptedEvent = sendEvent(&event); + + LOG(PluginEvents, "NPP_HandleEvent(mouseExited): %d", acceptedEvent); +} + +void WebNetscapePluginEventHandlerCarbon::mouseDragged(NSEvent*) +{ +} + +void WebNetscapePluginEventHandlerCarbon::mouseMoved(NSEvent* theEvent) +{ + EventRecord event; + + getCarbonEvent(&event, theEvent); + event.what = NPEventType_AdjustCursorEvent; + + BOOL acceptedEvent; + acceptedEvent = sendEvent(&event); + + LOG(PluginEvents, "NPP_HandleEvent(mouseMoved): %d", acceptedEvent); +} + +void WebNetscapePluginEventHandlerCarbon::keyDown(NSEvent *theEvent) +{ + m_suspendKeyUpEvents = true; + WKSendKeyEventToTSM(theEvent); +} + +void WebNetscapePluginEventHandlerCarbon::syntheticKeyDownWithCommandModifier(int keyCode, char character) +{ + EventRecord event; + getCarbonEvent(&event); + + event.what = ::keyDown; + event.modifiers |= cmdKey; + event.message = keyCode << 8 | character; + sendEvent(&event); +} + +static UInt32 keyMessageForEvent(NSEvent *event) +{ + NSData *data = [[event characters] dataUsingEncoding:CFStringConvertEncodingToNSStringEncoding(CFStringGetSystemEncoding())]; + if (!data) + return 0; + + UInt8 characterCode; + [data getBytes:&characterCode length:1]; + UInt16 keyCode = [event keyCode]; + return keyCode << 8 | characterCode; +} + +void WebNetscapePluginEventHandlerCarbon::keyUp(NSEvent* theEvent) +{ + WKSendKeyEventToTSM(theEvent); + + // TSM won't send keyUp events so we have to send them ourselves. + // Only send keyUp events after we receive the TSM callback because this is what plug-in expect from OS 9. + if (!m_suspendKeyUpEvents) { + EventRecord event; + + getCarbonEvent(&event, theEvent); + event.what = ::keyUp; + + if (event.message == 0) + event.message = keyMessageForEvent(theEvent); + + sendEvent(&event); + } +} + +void WebNetscapePluginEventHandlerCarbon::flagsChanged(NSEvent*) +{ +} + +void WebNetscapePluginEventHandlerCarbon::focusChanged(bool hasFocus) +{ + EventRecord event; + + getCarbonEvent(&event); + bool acceptedEvent; + if (hasFocus) { + event.what = NPEventType_GetFocusEvent; + acceptedEvent = sendEvent(&event); + LOG(PluginEvents, "NPP_HandleEvent(NPEventType_GetFocusEvent): %d", acceptedEvent); + installKeyEventHandler(); + } else { + event.what = NPEventType_LoseFocusEvent; + acceptedEvent = sendEvent(&event); + LOG(PluginEvents, "NPP_HandleEvent(NPEventType_LoseFocusEvent): %d", acceptedEvent); + removeKeyEventHandler(); + } +} + +void WebNetscapePluginEventHandlerCarbon::windowFocusChanged(bool hasFocus) +{ + WindowRef windowRef = (WindowRef)[[m_pluginView window] windowRef]; + + SetUserFocusWindow(windowRef); + + EventRecord event; + + getCarbonEvent(&event); + event.what = activateEvt; + event.message = (unsigned long)windowRef; + if (hasFocus) + event.modifiers |= activeFlag; + + BOOL acceptedEvent; + acceptedEvent = sendEvent(&event); + + LOG(PluginEvents, "NPP_HandleEvent(activateEvent): %d isActive: %d", acceptedEvent, hasFocus); +} + +OSStatus WebNetscapePluginEventHandlerCarbon::TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *eventHandler) +{ + EventRef rawKeyEventRef; + OSStatus status = GetEventParameter(inEvent, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof(EventRef), NULL, &rawKeyEventRef); + if (status != noErr) { + LOG_ERROR("GetEventParameter failed with error: %d", status); + return noErr; + } + + // Two-pass read to allocate/extract Mac charCodes + ByteCount numBytes; + status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, 0, &numBytes, NULL); + if (status != noErr) { + LOG_ERROR("GetEventParameter failed with error: %d", status); + return noErr; + } + char *buffer = (char *)malloc(numBytes); + status = GetEventParameter(rawKeyEventRef, kEventParamKeyMacCharCodes, typeChar, NULL, numBytes, NULL, buffer); + if (status != noErr) { + LOG_ERROR("GetEventParameter failed with error: %d", status); + free(buffer); + return noErr; + } + + EventRef cloneEvent = CopyEvent(rawKeyEventRef); + unsigned i; + for (i = 0; i < numBytes; i++) { + status = SetEventParameter(cloneEvent, kEventParamKeyMacCharCodes, typeChar, 1 /* one char code */, &buffer[i]); + if (status != noErr) { + LOG_ERROR("SetEventParameter failed with error: %d", status); + free(buffer); + return noErr; + } + + EventRecord eventRec; + if (ConvertEventRefToEventRecord(cloneEvent, &eventRec)) { + BOOL acceptedEvent; + acceptedEvent = static_cast<WebNetscapePluginEventHandlerCarbon*>(eventHandler)->sendEvent(&eventRec); + + LOG(PluginEvents, "NPP_HandleEvent(keyDown): %d charCode:%c keyCode:%lu", + acceptedEvent, (char) (eventRec.message & charCodeMask), (eventRec.message & keyCodeMask)); + + // We originally thought that if the plug-in didn't accept this event, + // we should pass it along so that keyboard scrolling, for example, will work. + // In practice, this is not a good idea, because plug-ins tend to eat the event but return false. + // MacIE handles each key event twice because of this, but we will emulate the other browsers instead. + } + } + ReleaseEvent(cloneEvent); + + free(buffer); + + return noErr; +} + +void WebNetscapePluginEventHandlerCarbon::installKeyEventHandler() +{ + static const EventTypeSpec sTSMEvents[] = + { + { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } + }; + + if (!m_keyEventHandler) { + InstallEventHandler(GetWindowEventTarget((WindowRef)[[m_pluginView window] windowRef]), + NewEventHandlerUPP(TSMEventHandler), + GetEventTypeCount(sTSMEvents), + sTSMEvents, + this, + &m_keyEventHandler); + } +} + +void WebNetscapePluginEventHandlerCarbon::removeKeyEventHandler() +{ + if (m_keyEventHandler) { + RemoveEventHandler(m_keyEventHandler); + m_keyEventHandler = 0; + } +} + +void WebNetscapePluginEventHandlerCarbon::nullEventTimerFired(CFRunLoopTimerRef timerRef, void *context) +{ + static_cast<WebNetscapePluginEventHandlerCarbon*>(context)->sendNullEvent(); +} + +void WebNetscapePluginEventHandlerCarbon::startTimers(bool throttleTimers) +{ + ASSERT(!m_nullEventTimer); + + CFTimeInterval interval = !throttleTimers ? NullEventIntervalActive : NullEventIntervalNotActive; + + CFRunLoopTimerContext context = { 0, this, NULL, NULL, NULL }; + m_nullEventTimer.adoptCF(CFRunLoopTimerCreate(0, CFAbsoluteTimeGetCurrent() + interval, interval, + 0, 0, nullEventTimerFired, &context)); + CFRunLoopAddTimer(CFRunLoopGetCurrent(), m_nullEventTimer.get(), kCFRunLoopDefaultMode); +} + +void WebNetscapePluginEventHandlerCarbon::stopTimers() +{ + if (!m_nullEventTimer) + return; + + CFRunLoopTimerInvalidate(m_nullEventTimer.get()); + m_nullEventTimer = 0; +} + +void* WebNetscapePluginEventHandlerCarbon::platformWindow(NSWindow* window) +{ + return [window windowRef]; +} + +bool WebNetscapePluginEventHandlerCarbon::sendEvent(EventRecord* event) +{ + // If at any point the user clicks or presses a key from within a plugin, set the + // currentEventIsUserGesture flag to true. This is important to differentiate legitimate + // window.open() calls; we still want to allow those. See rdar://problem/4010765 + if (event->what == ::mouseDown || event->what == ::keyDown || event->what == ::mouseUp || event->what == ::autoKey) + m_currentEventIsUserGesture = true; + + m_suspendKeyUpEvents = false; + + bool result = [m_pluginView sendEvent:event isDrawRect:event->what == updateEvt]; + + m_currentEventIsUserGesture = false; + + return result; +} + +#endif // ENABLE(NETSCAPE_PLUGIN_API) && !defined(__LP64__) diff --git a/Source/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCocoa.h b/Source/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCocoa.h new file mode 100644 index 0000000..3a2f03f --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCocoa.h @@ -0,0 +1,80 @@ +/* + * 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. + * + * 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. + */ + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#ifndef WebNetscapePluginEventHandlerCocoa_h +#define WebNetscapePluginEventHandlerCocoa_h + +#include <WebKit/npapi.h> +#include "WebNetscapePluginEventHandler.h" + +class WebNetscapePluginEventHandlerCocoa : public WebNetscapePluginEventHandler { +public: + WebNetscapePluginEventHandlerCocoa(WebNetscapePluginView*); + + virtual void drawRect(CGContextRef, const NSRect&); + + virtual void mouseDown(NSEvent*); + virtual void mouseDragged(NSEvent*); + virtual void mouseEntered(NSEvent*); + virtual void mouseExited(NSEvent*); + virtual void mouseMoved(NSEvent*); + virtual void mouseUp(NSEvent*); + virtual bool scrollWheel(NSEvent*); + + virtual void keyDown(NSEvent*); + virtual void keyUp(NSEvent*); + virtual void flagsChanged(NSEvent*); + virtual void syntheticKeyDownWithCommandModifier(int keyCode, char character); + + virtual void windowFocusChanged(bool hasFocus); + virtual void focusChanged(bool hasFocus); + + virtual void* platformWindow(NSWindow*); + +private: + bool sendMouseEvent(NSEvent*, NPCocoaEventType); + bool sendKeyEvent(NSEvent*, NPCocoaEventType); + bool sendEvent(NPCocoaEvent*); + +#ifndef __LP64__ + void installKeyEventHandler(); + void removeKeyEventHandler(); + + static OSStatus TSMEventHandler(EventHandlerCallRef, EventRef, void *eventHandler); + OSStatus handleTSMEvent(EventRef); + + EventHandlerRef m_keyEventHandler; +#else + inline void installKeyEventHandler() { } + void removeKeyEventHandler() { } +#endif +}; + +#endif //WebNetscapePluginEventHandlerCocoa_h + +#endif // ENABLE(NETSCAPE_PLUGIN_API) + diff --git a/Source/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCocoa.mm b/Source/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCocoa.mm new file mode 100644 index 0000000..a8ad861 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCocoa.mm @@ -0,0 +1,303 @@ +/* + * 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. + * + * 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. + */ + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#import "WebNetscapePluginEventHandlerCocoa.h" + +#import "WebKitSystemInterface.h" +#import "WebNetscapePluginView.h" +#import <wtf/UnusedParam.h> +#import <wtf/Vector.h> + +WebNetscapePluginEventHandlerCocoa::WebNetscapePluginEventHandlerCocoa(WebNetscapePluginView* pluginView) + : WebNetscapePluginEventHandler(pluginView) +#ifndef __LP64__ + , m_keyEventHandler(0) +#endif +{ +} + +static inline void initializeEvent(NPCocoaEvent* event, NPCocoaEventType type) +{ + event->type = type; + event->version = 0; +} + +void WebNetscapePluginEventHandlerCocoa::drawRect(CGContextRef context, const NSRect& rect) +{ + NPCocoaEvent event; + + initializeEvent(&event, NPCocoaEventDrawRect); + event.data.draw.context = context; + event.data.draw.x = rect.origin.x; + event.data.draw.y = rect.origin.y; + event.data.draw.width = rect.size.width; + event.data.draw.height = rect.size.height; + + RetainPtr<CGContextRef> protect(context); + + sendEvent(&event); +} + +void WebNetscapePluginEventHandlerCocoa::mouseDown(NSEvent *event) +{ + sendMouseEvent(event, NPCocoaEventMouseDown); +} + +void WebNetscapePluginEventHandlerCocoa::mouseDragged(NSEvent *event) +{ + sendMouseEvent(event, NPCocoaEventMouseDragged); +} + +void WebNetscapePluginEventHandlerCocoa::mouseEntered(NSEvent *event) +{ + sendMouseEvent(event, NPCocoaEventMouseEntered); +} + +void WebNetscapePluginEventHandlerCocoa::mouseExited(NSEvent *event) +{ + sendMouseEvent(event, NPCocoaEventMouseExited); +} + +void WebNetscapePluginEventHandlerCocoa::mouseMoved(NSEvent *event) +{ + sendMouseEvent(event, NPCocoaEventMouseMoved); +} + +void WebNetscapePluginEventHandlerCocoa::mouseUp(NSEvent *event) +{ + sendMouseEvent(event, NPCocoaEventMouseUp); +} + +bool WebNetscapePluginEventHandlerCocoa::scrollWheel(NSEvent* event) +{ + return sendMouseEvent(event, NPCocoaEventScrollWheel); +} + +bool WebNetscapePluginEventHandlerCocoa::sendMouseEvent(NSEvent *nsEvent, NPCocoaEventType type) +{ + NPCocoaEvent event; + + NSPoint point = [m_pluginView convertPoint:[nsEvent locationInWindow] fromView:nil]; + + int clickCount; + if (type == NPCocoaEventMouseEntered || type == NPCocoaEventMouseExited || type == NPCocoaEventScrollWheel) + clickCount = 0; + else + clickCount = [nsEvent clickCount]; + + initializeEvent(&event, type); + event.data.mouse.modifierFlags = [nsEvent modifierFlags]; + event.data.mouse.buttonNumber = [nsEvent buttonNumber]; + event.data.mouse.clickCount = clickCount; + event.data.mouse.pluginX = point.x; + event.data.mouse.pluginY = point.y; + event.data.mouse.deltaX = [nsEvent deltaX]; + event.data.mouse.deltaY = [nsEvent deltaY]; + event.data.mouse.deltaZ = [nsEvent deltaZ]; + + return sendEvent(&event); +} + +void WebNetscapePluginEventHandlerCocoa::keyDown(NSEvent *event) +{ + bool retval = sendKeyEvent(event, NPCocoaEventKeyDown); + +#ifndef __LP64__ + // If the plug-in did not handle the event, pass it on to the Input Manager. + if (retval) + WKSendKeyEventToTSM(event); +#else + UNUSED_PARAM(retval); +#endif +} + +void WebNetscapePluginEventHandlerCocoa::keyUp(NSEvent *event) +{ + sendKeyEvent(event, NPCocoaEventKeyUp); +} + +void WebNetscapePluginEventHandlerCocoa::flagsChanged(NSEvent *nsEvent) +{ + NPCocoaEvent event; + + initializeEvent(&event, NPCocoaEventFlagsChanged); + event.data.key.modifierFlags = [nsEvent modifierFlags]; + event.data.key.keyCode = [nsEvent keyCode]; + event.data.key.isARepeat = false; + event.data.key.characters = 0; + event.data.key.charactersIgnoringModifiers = 0; + + sendEvent(&event); +} + +void WebNetscapePluginEventHandlerCocoa::syntheticKeyDownWithCommandModifier(int keyCode, char character) +{ + char nullTerminatedString[] = { character, '\0' }; + + RetainPtr<NSString> characters(AdoptNS, [[NSString alloc] initWithUTF8String:nullTerminatedString]); + + NPCocoaEvent event; + initializeEvent(&event, NPCocoaEventKeyDown); + event.data.key.modifierFlags = NSCommandKeyMask; + event.data.key.keyCode = keyCode; + event.data.key.isARepeat = false; + event.data.key.characters = (NPNSString *)characters.get(); + event.data.key.charactersIgnoringModifiers = (NPNSString *)characters.get(); + + sendEvent(&event); +} + +bool WebNetscapePluginEventHandlerCocoa::sendKeyEvent(NSEvent* nsEvent, NPCocoaEventType type) +{ + NPCocoaEvent event; + + initializeEvent(&event, type); + event.data.key.modifierFlags = [nsEvent modifierFlags]; + event.data.key.keyCode = [nsEvent keyCode]; + event.data.key.isARepeat = [nsEvent isARepeat]; + event.data.key.characters = (NPNSString *)[nsEvent characters]; + event.data.key.charactersIgnoringModifiers = (NPNSString *)[nsEvent charactersIgnoringModifiers]; + + return sendEvent(&event); +} + +void WebNetscapePluginEventHandlerCocoa::windowFocusChanged(bool hasFocus) +{ + NPCocoaEvent event; + + initializeEvent(&event, NPCocoaEventWindowFocusChanged); + event.data.focus.hasFocus = hasFocus; + + sendEvent(&event); +} + +void WebNetscapePluginEventHandlerCocoa::focusChanged(bool hasFocus) +{ + NPCocoaEvent event; + + initializeEvent(&event, NPCocoaEventFocusChanged); + event.data.focus.hasFocus = hasFocus; + + sendEvent(&event); + + if (hasFocus) + installKeyEventHandler(); + else + removeKeyEventHandler(); +} + +void* WebNetscapePluginEventHandlerCocoa::platformWindow(NSWindow* window) +{ + return window; +} + +bool WebNetscapePluginEventHandlerCocoa::sendEvent(NPCocoaEvent* event) +{ + switch (event->type) { + case NPCocoaEventMouseDown: + case NPCocoaEventMouseUp: + case NPCocoaEventMouseDragged: + case NPCocoaEventKeyDown: + case NPCocoaEventKeyUp: + case NPCocoaEventFlagsChanged: + case NPCocoaEventScrollWheel: + m_currentEventIsUserGesture = true; + break; + default: + m_currentEventIsUserGesture = false; + } + + bool result = [m_pluginView sendEvent:event isDrawRect:event->type == NPCocoaEventDrawRect]; + + m_currentEventIsUserGesture = false; + return result; +} + +#ifndef __LP64__ + +void WebNetscapePluginEventHandlerCocoa::installKeyEventHandler() +{ + static const EventTypeSpec TSMEvents[] = + { + { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } + }; + + if (!m_keyEventHandler) + InstallEventHandler(GetWindowEventTarget((WindowRef)[[m_pluginView window] windowRef]), + NewEventHandlerUPP(TSMEventHandler), + GetEventTypeCount(TSMEvents), + TSMEvents, + this, + &m_keyEventHandler); +} + +void WebNetscapePluginEventHandlerCocoa::removeKeyEventHandler() +{ + if (m_keyEventHandler) { + RemoveEventHandler(m_keyEventHandler); + m_keyEventHandler = 0; + } +} + +OSStatus WebNetscapePluginEventHandlerCocoa::TSMEventHandler(EventHandlerCallRef inHandlerRef, EventRef event, void* eventHandler) +{ + return static_cast<WebNetscapePluginEventHandlerCocoa*>(eventHandler)->handleTSMEvent(event); +} + +OSStatus WebNetscapePluginEventHandlerCocoa::handleTSMEvent(EventRef eventRef) +{ + ASSERT(GetEventKind(eventRef) == kEventTextInputUnicodeForKeyEvent); + + // Get the text buffer size. + ByteCount size; + OSStatus result = GetEventParameter(eventRef, kEventParamTextInputSendText, typeUnicodeText, 0, 0, &size, 0); + if (result != noErr) + return result; + + unsigned length = size / sizeof(UniChar); + Vector<UniChar, 16> characters(length); + + // Now get the actual text. + result = GetEventParameter(eventRef, kEventParamTextInputSendText, typeUnicodeText, 0, size, 0, characters.data()); + if (result != noErr) + return result; + + RetainPtr<CFStringRef> text(AdoptCF, CFStringCreateWithCharacters(0, characters.data(), length)); + + NPCocoaEvent event; + + initializeEvent(&event, NPCocoaEventTextInput); + event.data.text.text = (NPNSString*)text.get(); + + sendEvent(&event); + + return noErr; +} + +#endif // __LP64__ + +#endif // ENABLE(NETSCAPE_PLUGIN_API) diff --git a/Source/WebKit/mac/Plugins/WebNetscapePluginPackage.h b/Source/WebKit/mac/Plugins/WebNetscapePluginPackage.h new file mode 100644 index 0000000..445c3bb --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebNetscapePluginPackage.h @@ -0,0 +1,87 @@ +/* + * 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. + */ + +#if ENABLE(NETSCAPE_PLUGIN_API) +#import "WebBasePluginPackage.h" + +#ifdef BUILDING_ON_TIGER +typedef short ResFileRefNum; +#endif + +#if defined(__ppc__) && !defined(__LP64__) +#define SUPPORT_CFM +#endif + +typedef enum { + WebCFMExecutableType, + WebMachOExecutableType +} WebExecutableType; + +@interface WebNetscapePluginPackage : WebBasePluginPackage +{ + NPPluginFuncs pluginFuncs; + NPNetscapeFuncs browserFuncs; + + uint16_t pluginSize; + uint16_t pluginVersion; + + ResFileRefNum resourceRef; + + NPP_ShutdownProcPtr NP_Shutdown; + + BOOL isLoaded; + BOOL needsUnload; + unsigned int instanceCount; + +#if USE(PLUGIN_HOST_PROCESS) + cpu_type_t pluginHostArchitecture; +#endif + +#ifdef SUPPORT_CFM + BOOL isBundle; + BOOL isCFM; + CFragConnectionID connID; +#endif +} + +// Netscape plug-in packages must be explicitly opened and closed by each plug-in instance. +// This is to protect Netscape plug-ins from being unloaded while they are in use. +- (void)open; +- (void)close; + +- (WebExecutableType)executableType; +- (NPPluginFuncs *)pluginFuncs; + +- (BOOL)supportsSnapshotting; + +#if USE(PLUGIN_HOST_PROCESS) +- (cpu_type_t)pluginHostArchitecture; +#endif + +@end +#endif diff --git a/Source/WebKit/mac/Plugins/WebNetscapePluginPackage.mm b/Source/WebKit/mac/Plugins/WebNetscapePluginPackage.mm new file mode 100644 index 0000000..7ca1121 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebNetscapePluginPackage.mm @@ -0,0 +1,782 @@ +/* + * 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. + */ + +#if ENABLE(NETSCAPE_PLUGIN_API) +#import "WebNetscapePluginPackage.h" + +#import "WebTypesInternal.h" +#import "WebKitLogging.h" +#import "WebKitNSStringExtras.h" +#import "WebNSFileManagerExtras.h" +#import "WebNSObjectExtras.h" +#import "WebNetscapeDeprecatedFunctions.h" +#import <WebCore/npruntime_impl.h> +#import <wtf/RetainPtr.h> + +#if USE(PLUGIN_HOST_PROCESS) +#import "NetscapePluginHostManager.h" + +using namespace WebKit; +#endif + +using namespace WebCore; + +#ifdef SUPPORT_CFM +typedef void (* FunctionPointer)(void); +typedef void (* TransitionVector)(void); +static FunctionPointer functionPointerForTVector(TransitionVector); +static TransitionVector tVectorForFunctionPointer(FunctionPointer); +#endif + +#define PluginNameOrDescriptionStringNumber 126 +#define MIMEDescriptionStringNumber 127 +#define MIMEListStringStringNumber 128 + +#define RealPlayerAppIndentifier @"com.RealNetworks.RealOne Player" +#define RealPlayerPluginFilename "RealPlayer Plugin" + +@interface WebNetscapePluginPackage (Internal) +- (void)_unloadWithShutdown:(BOOL)shutdown; +@end + +@implementation WebNetscapePluginPackage + +#ifndef __LP64__ ++ (void)initialize +{ + // The Shockwave plugin requires a valid file in CurApRefNum. + // But it doesn't seem to matter what file it is. + // If we're called inside a Cocoa application which won't have a + // CurApRefNum, we set it to point to the system resource file. + + // Call CurResFile before testing the result of WebLMGetCurApRefNum. + // If we are called before the bundle resource map has been opened + // for a Carbon application (or a Cocoa app with Resource Manager + // resources) we *do not* want to set CurApRefNum to point at the + // system resource file. CurResFile triggers Resource Manager lazy + // initialization, and will open the bundle resource map as necessary. + + CurResFile(); + + if (WebLMGetCurApRefNum() == -1) { + // To get the refNum for the system resource file, we have to do + // UseResFile(kSystemResFile) and then look at CurResFile(). + short savedCurResFile = CurResFile(); + UseResFile(kSystemResFile); + WebLMSetCurApRefNum(CurResFile()); + UseResFile(savedCurResFile); + } +} +#endif + +- (ResFileRefNum)openResourceFile +{ +#ifdef SUPPORT_CFM + if (!isBundle) { + FSRef fref; + OSErr err = FSPathMakeRef((const UInt8 *)[(NSString *)path fileSystemRepresentation], &fref, NULL); + if (err != noErr) + return -1; + + return FSOpenResFile(&fref, fsRdPerm); + } +#endif + + return CFBundleOpenBundleResourceMap(cfBundle.get()); +} + +- (void)closeResourceFile:(ResFileRefNum)resRef +{ +#ifdef SUPPORT_CFM + if (!isBundle) { + CloseResFile(resRef); + return; + } +#endif + + CFBundleCloseBundleResourceMap(cfBundle.get(), resRef); +} + +- (NSString *)stringForStringListID:(SInt16)stringListID andIndex:(SInt16)index +{ + // Get resource, and dereference the handle. + Handle stringHandle = Get1Resource('STR#', stringListID); + if (stringHandle == NULL) { + return nil; + } + unsigned char *p = (unsigned char *)*stringHandle; + if (!p) + return nil; + + // Check the index against the length of the string list, then skip the length. + if (index < 1 || index > *(SInt16 *)p) + return nil; + p += sizeof(SInt16); + + // Skip any strings that come before the one we are looking for. + while (--index) + p += 1 + *p; + + // Convert the one we found into an NSString. + return [[[NSString alloc] initWithBytes:(p + 1) length:*p encoding:[NSString _web_encodingForResource:stringHandle]] autorelease]; +} + +- (BOOL)getPluginInfoFromResources +{ + SInt16 resRef = [self openResourceFile]; + if (resRef == -1) + return NO; + + UseResFile(resRef); + if (ResError() != noErr) + return NO; + + NSString *MIME, *extensionsList, *description; + NSArray *extensions; + unsigned i; + + for (i=1; 1; i+=2) { + MIME = [[self stringForStringListID:MIMEListStringStringNumber + andIndex:i] lowercaseString]; + if (!MIME) + break; + + MimeClassInfo mimeClassInfo; + mimeClassInfo.type = String(MIME).lower(); + + extensionsList = [[self stringForStringListID:MIMEListStringStringNumber andIndex:i+1] lowercaseString]; + if (extensionsList) { + extensions = [extensionsList componentsSeparatedByString:@","]; + for (NSUInteger j = 0; j < [extensions count]; ++j) + mimeClassInfo.extensions.append((NSString *)[extensions objectAtIndex:j]); + } + + description = [self stringForStringListID:MIMEDescriptionStringNumber + andIndex:pluginInfo.mimes.size() + 1]; + mimeClassInfo.desc = description; + + pluginInfo.mimes.append(mimeClassInfo); + } + + NSString *filename = [(NSString *)path lastPathComponent]; + pluginInfo.file = filename; + + description = [self stringForStringListID:PluginNameOrDescriptionStringNumber andIndex:1]; + if (!description) + description = filename; + pluginInfo.desc = description; + + + NSString *theName = [self stringForStringListID:PluginNameOrDescriptionStringNumber andIndex:2]; + if (!theName) + theName = filename; + pluginInfo.name = theName; + + [self closeResourceFile:resRef]; + + return YES; +} + +- (BOOL)_initWithPath:(NSString *)pluginPath +{ + resourceRef = -1; + + OSType type = 0; + + if (cfBundle) { + // Bundle + CFBundleGetPackageInfo(cfBundle.get(), &type, NULL); +#ifdef SUPPORT_CFM + isBundle = YES; +#endif + } else { +#ifdef SUPPORT_CFM + // Single-file plug-in with resource fork + NSString *destinationPath = [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath:path error:0]; + type = [[[NSFileManager defaultManager] attributesOfItemAtPath:destinationPath error:0] fileHFSTypeCode]; + isBundle = NO; + isCFM = YES; +#else + return NO; +#endif + } + + if (type != FOUR_CHAR_CODE('BRPL')) + return NO; + + // Check if the executable is Mach-O or CFM. + if (cfBundle) { + RetainPtr<CFURLRef> executableURL(AdoptCF, CFBundleCopyExecutableURL(cfBundle.get())); + if (!executableURL) + return NO; + NSFileHandle *executableFile = [NSFileHandle fileHandleForReadingAtPath:[(NSURL *)executableURL.get() path]]; + NSData *data = [executableFile readDataOfLength:512]; + [executableFile closeFile]; + // Check the length of the data before calling memcmp. We think this fixes 3782543. + if (data == nil || [data length] < 8) + return NO; + BOOL hasCFMHeader = memcmp([data bytes], "Joy!peff", 8) == 0; +#ifdef SUPPORT_CFM + isCFM = hasCFMHeader; +#else + if (hasCFMHeader) + return NO; +#endif + +#if USE(PLUGIN_HOST_PROCESS) + RetainPtr<CFArrayRef> archs(AdoptCF, CFBundleCopyExecutableArchitectures(cfBundle.get())); + + if ([(NSArray *)archs.get() containsObject:[NSNumber numberWithInteger:NSBundleExecutableArchitectureX86_64]]) + pluginHostArchitecture = CPU_TYPE_X86_64; + else if ([(NSArray *)archs.get() containsObject:[NSNumber numberWithInteger:NSBundleExecutableArchitectureI386]]) + pluginHostArchitecture = CPU_TYPE_X86; + else + return NO; +#else + if (![self isNativeLibraryData:data]) + return NO; +#endif + } + + if (![self getPluginInfoFromPLists] && ![self getPluginInfoFromResources]) + return NO; + + return YES; +} + +- (id)initWithPath:(NSString *)pluginPath +{ + if (!(self = [super initWithPath:pluginPath])) + return nil; + + // Initializing a plugin package can cause it to be loaded. If there was an error initializing the plugin package, + // ensure that it is unloaded before deallocating it (WebBasePluginPackage requires & asserts this). + if (![self _initWithPath:pluginPath]) { + [self _unloadWithShutdown:YES]; + [self release]; + return nil; + } + + return self; +} + +- (WebExecutableType)executableType +{ +#ifdef SUPPORT_CFM + if (isCFM) + return WebCFMExecutableType; +#endif + return WebMachOExecutableType; +} + +#if USE(PLUGIN_HOST_PROCESS) +- (cpu_type_t)pluginHostArchitecture +{ + return pluginHostArchitecture; +} + +- (void)createPropertyListFile +{ + NetscapePluginHostManager::createPropertyListFile(path, pluginHostArchitecture); +} + +#endif + +- (void)launchRealPlayer +{ + CFURLRef appURL = NULL; + OSStatus error = LSFindApplicationForInfo(kLSUnknownCreator, (CFStringRef)RealPlayerAppIndentifier, NULL, NULL, &appURL); + if (!error) { + LSLaunchURLSpec URLSpec; + bzero(&URLSpec, sizeof(URLSpec)); + URLSpec.launchFlags = kLSLaunchDefaults | kLSLaunchDontSwitch; + URLSpec.appURL = appURL; + LSOpenFromURLSpec(&URLSpec, NULL); + CFRelease(appURL); + } +} + +- (void)_applyDjVuWorkaround +{ + if (!cfBundle) + return; + + if ([self bundleIdentifier] == "com.lizardtech.NPDjVu") { + // The DjVu plug-in will crash copying the vtable if it's too big so we cap it to + // what the plug-in expects here. + // size + version + 40 function pointers. + browserFuncs.size = 2 + 2 + sizeof(void *) * 40; + } + +} + +- (void)unload +{ + [self _unloadWithShutdown:YES]; +} + +- (BOOL)_tryLoad +{ + NP_GetEntryPointsFuncPtr NP_GetEntryPoints = NULL; + NP_InitializeFuncPtr NP_Initialize = NULL; + NPError npErr; + +#ifdef SUPPORT_CFM + MainFuncPtr pluginMainFunc = NULL; +#endif + +#if !LOG_DISABLED + CFAbsoluteTime start = CFAbsoluteTimeGetCurrent(); + CFAbsoluteTime currentTime; + CFAbsoluteTime duration; +#endif + LOG(Plugins, "%f Load timing started for: %@", start, (NSString *)[self pluginInfo].name); + + if (isLoaded) + return YES; + +#ifdef SUPPORT_CFM + if (isBundle) { +#endif + if (!CFBundleLoadExecutable(cfBundle.get())) + return NO; +#if !LOG_DISABLED + currentTime = CFAbsoluteTimeGetCurrent(); + duration = currentTime - start; +#endif + LOG(Plugins, "%f CFBundleLoadExecutable took %f seconds", currentTime, duration); + isLoaded = YES; + +#ifdef SUPPORT_CFM + if (isCFM) { + pluginMainFunc = (MainFuncPtr)CFBundleGetFunctionPointerForName(cfBundle.get(), CFSTR("main") ); + if (!pluginMainFunc) + return NO; + } else { +#endif + NP_Initialize = (NP_InitializeFuncPtr)CFBundleGetFunctionPointerForName(cfBundle.get(), CFSTR("NP_Initialize")); + NP_GetEntryPoints = (NP_GetEntryPointsFuncPtr)CFBundleGetFunctionPointerForName(cfBundle.get(), CFSTR("NP_GetEntryPoints")); + NP_Shutdown = (NPP_ShutdownProcPtr)CFBundleGetFunctionPointerForName(cfBundle.get(), CFSTR("NP_Shutdown")); + if (!NP_Initialize || !NP_GetEntryPoints || !NP_Shutdown) + return NO; +#ifdef SUPPORT_CFM + } + } else { + // single CFM file + FSSpec spec; + FSRef fref; + OSErr err; + + err = FSPathMakeRef((UInt8 *)[(NSString *)path fileSystemRepresentation], &fref, NULL); + if (err != noErr) { + LOG_ERROR("FSPathMakeRef failed. Error=%d", err); + return NO; + } + err = FSGetCatalogInfo(&fref, kFSCatInfoNone, NULL, NULL, &spec, NULL); + if (err != noErr) { + LOG_ERROR("FSGetCatalogInfo failed. Error=%d", err); + return NO; + } + err = WebGetDiskFragment(&spec, 0, kCFragGoesToEOF, nil, kPrivateCFragCopy, &connID, (Ptr *)&pluginMainFunc, nil); + if (err != noErr) { + LOG_ERROR("WebGetDiskFragment failed. Error=%d", err); + return NO; + } +#if !LOG_DISABLED + currentTime = CFAbsoluteTimeGetCurrent(); + duration = currentTime - start; +#endif + LOG(Plugins, "%f WebGetDiskFragment took %f seconds", currentTime, duration); + isLoaded = YES; + + pluginMainFunc = (MainFuncPtr)functionPointerForTVector((TransitionVector)pluginMainFunc); + if (!pluginMainFunc) { + return NO; + } + + // NOTE: pluginMainFunc is freed after it is called. Be sure not to return before that. + + isCFM = YES; + } +#endif /* SUPPORT_CFM */ + + // Plugins (at least QT) require that you call UseResFile on the resource file before loading it. + resourceRef = [self openResourceFile]; + if (resourceRef != -1) { + UseResFile(resourceRef); + } + + // swap function tables +#ifdef SUPPORT_CFM + if (isCFM) { + browserFuncs.version = NP_VERSION_MINOR; + browserFuncs.size = sizeof(NPNetscapeFuncs); + browserFuncs.geturl = (NPN_GetURLProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_GetURL); + browserFuncs.posturl = (NPN_PostURLProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_PostURL); + browserFuncs.requestread = (NPN_RequestReadProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_RequestRead); + browserFuncs.newstream = (NPN_NewStreamProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_NewStream); + browserFuncs.write = (NPN_WriteProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_Write); + browserFuncs.destroystream = (NPN_DestroyStreamProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_DestroyStream); + browserFuncs.status = (NPN_StatusProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_Status); + browserFuncs.uagent = (NPN_UserAgentProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_UserAgent); + browserFuncs.memalloc = (NPN_MemAllocProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_MemAlloc); + browserFuncs.memfree = (NPN_MemFreeProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_MemFree); + browserFuncs.memflush = (NPN_MemFlushProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_MemFlush); + browserFuncs.reloadplugins = (NPN_ReloadPluginsProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_ReloadPlugins); + browserFuncs.geturlnotify = (NPN_GetURLNotifyProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_GetURLNotify); + browserFuncs.posturlnotify = (NPN_PostURLNotifyProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_PostURLNotify); + browserFuncs.getvalue = (NPN_GetValueProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_GetValue); + browserFuncs.setvalue = (NPN_SetValueProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_SetValue); + browserFuncs.invalidaterect = (NPN_InvalidateRectProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_InvalidateRect); + browserFuncs.invalidateregion = (NPN_InvalidateRegionProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_InvalidateRegion); + browserFuncs.forceredraw = (NPN_ForceRedrawProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_ForceRedraw); + browserFuncs.getJavaEnv = (NPN_GetJavaEnvProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_GetJavaEnv); + browserFuncs.getJavaPeer = (NPN_GetJavaPeerProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_GetJavaPeer); + browserFuncs.pushpopupsenabledstate = (NPN_PushPopupsEnabledStateProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_PushPopupsEnabledState); + browserFuncs.poppopupsenabledstate = (NPN_PopPopupsEnabledStateProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_PopPopupsEnabledState); + browserFuncs.pluginthreadasynccall = (NPN_PluginThreadAsyncCallProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_PluginThreadAsyncCall); + browserFuncs.getvalueforurl = (NPN_GetValueForURLProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_GetValueForURL); + browserFuncs.setvalueforurl = (NPN_SetValueForURLProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_SetValueForURL); + browserFuncs.getauthenticationinfo = (NPN_GetAuthenticationInfoProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_GetAuthenticationInfo); + browserFuncs.scheduletimer = (NPN_ScheduleTimerProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_ScheduleTimer); + browserFuncs.unscheduletimer = (NPN_UnscheduleTimerProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_UnscheduleTimer); + browserFuncs.popupcontextmenu = (NPN_PopUpContextMenuProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_PopUpContextMenu); + browserFuncs.convertpoint = (NPN_ConvertPointProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_ConvertPoint); + + browserFuncs.releasevariantvalue = (NPN_ReleaseVariantValueProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_ReleaseVariantValue); + browserFuncs.getstringidentifier = (NPN_GetStringIdentifierProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_GetStringIdentifier); + browserFuncs.getstringidentifiers = (NPN_GetStringIdentifiersProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_GetStringIdentifiers); + browserFuncs.getintidentifier = (NPN_GetIntIdentifierProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_GetIntIdentifier); + browserFuncs.identifierisstring = (NPN_IdentifierIsStringProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_IdentifierIsString); + browserFuncs.utf8fromidentifier = (NPN_UTF8FromIdentifierProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_UTF8FromIdentifier); + browserFuncs.intfromidentifier = (NPN_IntFromIdentifierProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_IntFromIdentifier); + browserFuncs.createobject = (NPN_CreateObjectProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_CreateObject); + browserFuncs.retainobject = (NPN_RetainObjectProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_RetainObject); + browserFuncs.releaseobject = (NPN_ReleaseObjectProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_ReleaseObject); + browserFuncs.hasmethod = (NPN_HasMethodProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_HasProperty); + browserFuncs.invoke = (NPN_InvokeProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_Invoke); + browserFuncs.invokeDefault = (NPN_InvokeDefaultProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_InvokeDefault); + browserFuncs.evaluate = (NPN_EvaluateProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_Evaluate); + browserFuncs.hasproperty = (NPN_HasPropertyProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_HasProperty); + browserFuncs.getproperty = (NPN_GetPropertyProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_GetProperty); + browserFuncs.setproperty = (NPN_SetPropertyProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_SetProperty); + browserFuncs.removeproperty = (NPN_RemovePropertyProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_RemoveProperty); + browserFuncs.setexception = (NPN_SetExceptionProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_SetException); + browserFuncs.enumerate = (NPN_EnumerateProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_Enumerate); + browserFuncs.construct = (NPN_ConstructProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_Construct); + + [self _applyDjVuWorkaround]; + +#if !LOG_DISABLED + CFAbsoluteTime mainStart = CFAbsoluteTimeGetCurrent(); +#endif + LOG(Plugins, "%f main timing started", mainStart); + NPP_ShutdownProcPtr shutdownFunction; + npErr = pluginMainFunc(&browserFuncs, &pluginFuncs, &shutdownFunction); + NP_Shutdown = (NPP_ShutdownProcPtr)functionPointerForTVector((TransitionVector)shutdownFunction); + if (!isBundle) + // Don't free pluginMainFunc if we got it from a bundle because it is owned by CFBundle in that case. + free(reinterpret_cast<void*>(pluginMainFunc)); + + // Workaround for 3270576. The RealPlayer plug-in fails to load if its preference file is out of date. + // Launch the RealPlayer application to refresh the file. + if (npErr != NPERR_NO_ERROR) { + if (npErr == NPERR_MODULE_LOAD_FAILED_ERROR && equalIgnoringCase(pluginInfo.file, RealPlayerPluginFilename)) + [self launchRealPlayer]; + return NO; + } +#if !LOG_DISABLED + currentTime = CFAbsoluteTimeGetCurrent(); + duration = currentTime - mainStart; +#endif + LOG(Plugins, "%f main took %f seconds", currentTime, duration); + + pluginSize = pluginFuncs.size; + pluginVersion = pluginFuncs.version; + LOG(Plugins, "pluginMainFunc: %d, size=%d, version=%d", npErr, pluginSize, pluginVersion); + + pluginFuncs.newp = (NPP_NewProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.newp); + pluginFuncs.destroy = (NPP_DestroyProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.destroy); + pluginFuncs.setwindow = (NPP_SetWindowProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.setwindow); + pluginFuncs.newstream = (NPP_NewStreamProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.newstream); + pluginFuncs.destroystream = (NPP_DestroyStreamProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.destroystream); + pluginFuncs.asfile = (NPP_StreamAsFileProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.asfile); + pluginFuncs.writeready = (NPP_WriteReadyProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.writeready); + pluginFuncs.write = (NPP_WriteProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.write); + pluginFuncs.print = (NPP_PrintProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.print); + pluginFuncs.event = (NPP_HandleEventProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.event); + pluginFuncs.urlnotify = (NPP_URLNotifyProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.urlnotify); + pluginFuncs.getvalue = (NPP_GetValueProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.getvalue); + pluginFuncs.setvalue = (NPP_SetValueProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.setvalue); + + // LiveConnect support + pluginFuncs.javaClass = (JRIGlobalRef)functionPointerForTVector((TransitionVector)pluginFuncs.javaClass); + if (pluginFuncs.javaClass) { + LOG(LiveConnect, "%@: CFM entry point for NPP_GetJavaClass = %p", (NSString *)[self pluginInfo].name, pluginFuncs.javaClass); + } else { + LOG(LiveConnect, "%@: no entry point for NPP_GetJavaClass", (NSString *)[self pluginInfo].name); + } + + } else { + +#endif + + // no function pointer conversion necessary for Mach-O + browserFuncs.version = NP_VERSION_MINOR; + browserFuncs.size = sizeof(NPNetscapeFuncs); + browserFuncs.geturl = NPN_GetURL; + browserFuncs.posturl = NPN_PostURL; + browserFuncs.requestread = NPN_RequestRead; + browserFuncs.newstream = NPN_NewStream; + browserFuncs.write = NPN_Write; + browserFuncs.destroystream = NPN_DestroyStream; + browserFuncs.status = NPN_Status; + browserFuncs.uagent = NPN_UserAgent; + browserFuncs.memalloc = NPN_MemAlloc; + browserFuncs.memfree = NPN_MemFree; + browserFuncs.memflush = NPN_MemFlush; + browserFuncs.reloadplugins = NPN_ReloadPlugins; + browserFuncs.geturlnotify = NPN_GetURLNotify; + browserFuncs.posturlnotify = NPN_PostURLNotify; + browserFuncs.getvalue = NPN_GetValue; + browserFuncs.setvalue = NPN_SetValue; + browserFuncs.invalidaterect = NPN_InvalidateRect; + browserFuncs.invalidateregion = NPN_InvalidateRegion; + browserFuncs.forceredraw = NPN_ForceRedraw; + browserFuncs.getJavaEnv = NPN_GetJavaEnv; + browserFuncs.getJavaPeer = NPN_GetJavaPeer; + browserFuncs.pushpopupsenabledstate = NPN_PushPopupsEnabledState; + browserFuncs.poppopupsenabledstate = NPN_PopPopupsEnabledState; + browserFuncs.pluginthreadasynccall = NPN_PluginThreadAsyncCall; + browserFuncs.getvalueforurl = NPN_GetValueForURL; + browserFuncs.setvalueforurl = NPN_SetValueForURL; + browserFuncs.getauthenticationinfo = NPN_GetAuthenticationInfo; + browserFuncs.scheduletimer = NPN_ScheduleTimer; + browserFuncs.unscheduletimer = NPN_UnscheduleTimer; + browserFuncs.popupcontextmenu = NPN_PopUpContextMenu; + browserFuncs.convertpoint = NPN_ConvertPoint; + + browserFuncs.releasevariantvalue = _NPN_ReleaseVariantValue; + browserFuncs.getstringidentifier = _NPN_GetStringIdentifier; + browserFuncs.getstringidentifiers = _NPN_GetStringIdentifiers; + browserFuncs.getintidentifier = _NPN_GetIntIdentifier; + browserFuncs.identifierisstring = _NPN_IdentifierIsString; + browserFuncs.utf8fromidentifier = _NPN_UTF8FromIdentifier; + browserFuncs.intfromidentifier = _NPN_IntFromIdentifier; + browserFuncs.createobject = _NPN_CreateObject; + browserFuncs.retainobject = _NPN_RetainObject; + browserFuncs.releaseobject = _NPN_ReleaseObject; + browserFuncs.hasmethod = _NPN_HasMethod; + browserFuncs.invoke = _NPN_Invoke; + browserFuncs.invokeDefault = _NPN_InvokeDefault; + browserFuncs.evaluate = _NPN_Evaluate; + browserFuncs.hasproperty = _NPN_HasProperty; + browserFuncs.getproperty = _NPN_GetProperty; + browserFuncs.setproperty = _NPN_SetProperty; + browserFuncs.removeproperty = _NPN_RemoveProperty; + browserFuncs.setexception = _NPN_SetException; + browserFuncs.enumerate = _NPN_Enumerate; + browserFuncs.construct = _NPN_Construct; + + [self _applyDjVuWorkaround]; + +#if !LOG_DISABLED + CFAbsoluteTime initializeStart = CFAbsoluteTimeGetCurrent(); +#endif + LOG(Plugins, "%f NP_Initialize timing started", initializeStart); + npErr = NP_Initialize(&browserFuncs); + if (npErr != NPERR_NO_ERROR) + return NO; +#if !LOG_DISABLED + currentTime = CFAbsoluteTimeGetCurrent(); + duration = currentTime - initializeStart; +#endif + LOG(Plugins, "%f NP_Initialize took %f seconds", currentTime, duration); + + pluginFuncs.size = sizeof(NPPluginFuncs); + + npErr = NP_GetEntryPoints(&pluginFuncs); + if (npErr != NPERR_NO_ERROR) + return NO; + + pluginSize = pluginFuncs.size; + pluginVersion = pluginFuncs.version; + + if (pluginFuncs.javaClass) + LOG(LiveConnect, "%@: mach-o entry point for NPP_GetJavaClass = %p", (NSString *)[self pluginInfo].name, pluginFuncs.javaClass); + else + LOG(LiveConnect, "%@: no entry point for NPP_GetJavaClass", (NSString *)[self pluginInfo].name); + +#ifdef SUPPORT_CFM + } +#endif + +#if !LOG_DISABLED + currentTime = CFAbsoluteTimeGetCurrent(); + duration = currentTime - start; +#endif + LOG(Plugins, "%f Total load time: %f seconds", currentTime, duration); + + return YES; +} + +- (BOOL)load +{ + if ([self _tryLoad]) + return [super load]; + + [self _unloadWithShutdown:NO]; + return NO; +} + +- (NPPluginFuncs *)pluginFuncs +{ + return &pluginFuncs; +} + +- (void)wasRemovedFromPluginDatabase:(WebPluginDatabase *)database +{ + [super wasRemovedFromPluginDatabase:database]; + + // Unload when removed from final plug-in database + if ([pluginDatabases count] == 0) + [self _unloadWithShutdown:YES]; +} + +- (void)open +{ + instanceCount++; + + // Handle the case where all instances close a plug-in package, but another + // instance opens the package before it is unloaded (which only happens when + // the plug-in database is refreshed) + needsUnload = NO; + + if (!isLoaded) { + // Should load when the first instance opens the plug-in package + ASSERT(instanceCount == 1); + [self load]; + } +} + +- (void)close +{ + ASSERT(instanceCount > 0); + instanceCount--; + if (instanceCount == 0 && needsUnload) + [self _unloadWithShutdown:YES]; +} + + +- (BOOL)supportsSnapshotting +{ + if ([self bundleIdentifier] != "com.macromedia.Flash Player.plugin") + return YES; + + // Flash has a bogus Info.plist entry for CFBundleVersionString, so use CFBundleShortVersionString. + NSString *versionString = (NSString *)CFDictionaryGetValue(CFBundleGetInfoDictionary(cfBundle.get()), CFSTR("CFBundleShortVersionString")); + + if (![versionString hasPrefix:@"10.1"]) + return YES; + + // Some prerelease versions of Flash 10.1 crash when sent a drawRect event using the CA drawing model: <rdar://problem/7739922> + return CFStringCompare((CFStringRef)versionString, CFSTR("10.1.53.60"), kCFCompareNumerically) != kCFCompareLessThan; +} + +@end + +#ifdef SUPPORT_CFM + +// function pointer converters + +FunctionPointer functionPointerForTVector(TransitionVector tvp) +{ + const uint32_t temp[6] = {0x3D800000, 0x618C0000, 0x800C0000, 0x804C0004, 0x7C0903A6, 0x4E800420}; + uint32_t *newGlue = NULL; + + if (tvp != NULL) { + newGlue = (uint32_t *)malloc(sizeof(temp)); + if (newGlue != NULL) { + unsigned i; + for (i = 0; i < 6; i++) newGlue[i] = temp[i]; + newGlue[0] |= ((uintptr_t)tvp >> 16); + newGlue[1] |= ((uintptr_t)tvp & 0xFFFF); + MakeDataExecutable(newGlue, sizeof(temp)); + } + } + + return (FunctionPointer)newGlue; +} + +TransitionVector tVectorForFunctionPointer(FunctionPointer fp) +{ + FunctionPointer *newGlue = NULL; + if (fp != NULL) { + newGlue = (FunctionPointer *)malloc(2 * sizeof(FunctionPointer)); + if (newGlue != NULL) { + newGlue[0] = fp; + newGlue[1] = NULL; + } + } + return (TransitionVector)newGlue; +} + +#endif + +@implementation WebNetscapePluginPackage (Internal) + +- (void)_unloadWithShutdown:(BOOL)shutdown +{ + if (!isLoaded) + return; + + LOG(Plugins, "Unloading %@...", (NSString *)pluginInfo.name); + + // Cannot unload a plug-in package while an instance is still using it + if (instanceCount > 0) { + needsUnload = YES; + return; + } + + if (shutdown && NP_Shutdown) + NP_Shutdown(); + + if (resourceRef != -1) + [self closeResourceFile:resourceRef]; + +#ifdef SUPPORT_CFM + if (!isBundle) + WebCloseConnection(&connID); +#endif + + LOG(Plugins, "Plugin Unloaded"); + isLoaded = NO; +} + +@end +#endif diff --git a/Source/WebKit/mac/Plugins/WebNetscapePluginStream.h b/Source/WebKit/mac/Plugins/WebNetscapePluginStream.h new file mode 100644 index 0000000..20beee2 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebNetscapePluginStream.h @@ -0,0 +1,133 @@ +/* + * 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. + */ + +#if ENABLE(NETSCAPE_PLUGIN_API) +#import <Foundation/Foundation.h> + +#import <WebCore/Timer.h> +#import <WebCore/NetscapePlugInStreamLoader.h> +#import <WebKit/npfunctions.h> +#import <wtf/PassRefPtr.h> +#import <wtf/RefCounted.h> +#import <wtf/RefPtr.h> +#import <wtf/RetainPtr.h> +#import <wtf/text/CString.h> + +#import "WebNetscapePluginView.h" + +namespace WebCore { + class FrameLoader; + class NetscapePlugInStreamLoader; +} + +@class WebNetscapePluginView; +@class NSURLResponse; + +class WebNetscapePluginStream : public RefCounted<WebNetscapePluginStream> + , private WebCore::NetscapePlugInStreamLoaderClient +{ +public: + static PassRefPtr<WebNetscapePluginStream> create(NSURLRequest *request, NPP plugin, bool sendNotification, void* notifyData) + { + return adoptRef(new WebNetscapePluginStream(request, plugin, sendNotification, notifyData)); + } + static PassRefPtr<WebNetscapePluginStream> create(WebCore::FrameLoader* frameLoader) + { + return adoptRef(new WebNetscapePluginStream(frameLoader)); + } + virtual ~WebNetscapePluginStream(); + + NPP plugin() const { return m_plugin; } + void setPlugin(NPP); + + static NPP ownerForStream(NPStream *); + + static NPReason reasonForError(NSError *); + NSError *errorForReason(NPReason) const; + + void cancelLoadAndDestroyStreamWithError(NSError *); + + void setRequestURL(const WebCore::KURL& requestURL) { m_requestURL = requestURL; } + + void start(); + void stop(); + + void startStreamWithResponse(NSURLResponse *response); + + void didReceiveData(WebCore::NetscapePlugInStreamLoader*, const char* bytes, int length); + void destroyStreamWithError(NSError *); + void didFinishLoading(WebCore::NetscapePlugInStreamLoader*); + +private: + void destroyStream(); + void cancelLoadWithError(NSError *); + void destroyStreamWithReason(NPReason); + void deliverDataToFile(NSData *data); + void deliverData(); + + void startStream(NSURL *, long long expectedContentLength, NSDate *lastModifiedDate, const WTF::String& mimeType, NSData *headers); + + NSError *pluginCancelledConnectionError() const; + + // NetscapePlugInStreamLoaderClient methods. + void didReceiveResponse(WebCore::NetscapePlugInStreamLoader*, const WebCore::ResourceResponse&); + void didFail(WebCore::NetscapePlugInStreamLoader*, const WebCore::ResourceError&); + bool wantsAllStreams() const; + + RetainPtr<NSMutableData> m_deliveryData; + WebCore::KURL m_requestURL; + RetainPtr<NSURL> m_responseURL; + CString m_mimeType; + + NPP m_plugin; + uint16_t m_transferMode; + int32_t m_offset; + NPStream m_stream; + RetainPtr<NSString> m_path; + int m_fileDescriptor; + BOOL m_sendNotification; + void *m_notifyData; + char *m_headers; + RetainPtr<WebNetscapePluginView> m_pluginView; + NPReason m_reason; + bool m_isTerminated; + bool m_newStreamSuccessful; + + WebCore::FrameLoader* m_frameLoader; + RefPtr<WebCore::NetscapePlugInStreamLoader> m_loader; + RetainPtr<NSMutableURLRequest> m_request; + NPPluginFuncs *m_pluginFuncs; + + void deliverDataTimerFired(WebCore::Timer<WebNetscapePluginStream>* timer); + WebCore::Timer<WebNetscapePluginStream> m_deliverDataTimer; + + WebNetscapePluginStream(WebCore::FrameLoader*); + WebNetscapePluginStream(NSURLRequest *, NPP, bool sendNotification, void* notifyData); +}; + +#endif diff --git a/Source/WebKit/mac/Plugins/WebNetscapePluginStream.mm b/Source/WebKit/mac/Plugins/WebNetscapePluginStream.mm new file mode 100644 index 0000000..c677973 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebNetscapePluginStream.mm @@ -0,0 +1,640 @@ +/* + * 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. + */ + +#if ENABLE(NETSCAPE_PLUGIN_API) +#import "WebNetscapePluginStream.h" + +#import "WebNetscapePluginView.h" +#import "WebFrameInternal.h" +#import "WebKitErrorsPrivate.h" +#import "WebKitLogging.h" +#import "WebNSObjectExtras.h" +#import "WebNSURLExtras.h" +#import "WebNSURLRequestExtras.h" +#import "WebNetscapePluginPackage.h" +#import <Foundation/NSURLResponse.h> +#import <runtime/JSLock.h> +#import <WebCore/DocumentLoader.h> +#import <WebCore/Frame.h> +#import <WebCore/FrameLoader.h> +#import <WebCore/ResourceLoadScheduler.h> +#import <WebCore/SecurityOrigin.h> +#import <WebCore/WebCoreObjCExtras.h> +#import <WebCore/WebCoreURLResponse.h> +#import <WebKitSystemInterface.h> +#import <wtf/HashMap.h> +#import <wtf/StdLibExtras.h> + +using namespace WebCore; +using namespace std; + +#define WEB_REASON_NONE -1 + +static NSString *CarbonPathFromPOSIXPath(NSString *posixPath); + +class PluginStopDeferrer { +public: + PluginStopDeferrer(WebNetscapePluginView* pluginView) + : m_pluginView(pluginView) + { + ASSERT(m_pluginView); + + [m_pluginView.get() willCallPlugInFunction]; + } + + ~PluginStopDeferrer() + { + ASSERT(m_pluginView); + [m_pluginView.get() didCallPlugInFunction]; + } + +private: + RetainPtr<WebNetscapePluginView> m_pluginView; +}; + +typedef HashMap<NPStream*, NPP> StreamMap; +static StreamMap& streams() +{ + DEFINE_STATIC_LOCAL(StreamMap, staticStreams, ()); + return staticStreams; +} + +NPP WebNetscapePluginStream::ownerForStream(NPStream *stream) +{ + return streams().get(stream); +} + +NPReason WebNetscapePluginStream::reasonForError(NSError *error) +{ + if (!error) + return NPRES_DONE; + + if ([[error domain] isEqualToString:NSURLErrorDomain] && [error code] == NSURLErrorCancelled) + return NPRES_USER_BREAK; + + return NPRES_NETWORK_ERR; +} + +NSError *WebNetscapePluginStream::pluginCancelledConnectionError() const +{ + return [[[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInCancelledConnection + contentURL:m_responseURL ? m_responseURL.get() : (NSURL *)m_requestURL + pluginPageURL:nil + pluginName:[[m_pluginView.get() pluginPackage] pluginInfo].name + MIMEType:(NSString *)String::fromUTF8(m_mimeType.data(), m_mimeType.length())] autorelease]; +} + +NSError *WebNetscapePluginStream::errorForReason(NPReason reason) const +{ + if (reason == NPRES_DONE) + return nil; + + if (reason == NPRES_USER_BREAK) + return [NSError _webKitErrorWithDomain:NSURLErrorDomain + code:NSURLErrorCancelled + URL:m_responseURL ? m_responseURL.get() : (NSURL *)m_requestURL]; + + return pluginCancelledConnectionError(); +} + +WebNetscapePluginStream::WebNetscapePluginStream(FrameLoader* frameLoader) + : m_plugin(0) + , m_transferMode(0) + , m_offset(0) + , m_fileDescriptor(-1) + , m_sendNotification(false) + , m_notifyData(0) + , m_headers(0) + , m_reason(NPRES_BASE) + , m_isTerminated(false) + , m_newStreamSuccessful(false) + , m_frameLoader(frameLoader) + , m_pluginFuncs(0) + , m_deliverDataTimer(this, &WebNetscapePluginStream::deliverDataTimerFired) +{ + memset(&m_stream, 0, sizeof(NPStream)); +} + +WebNetscapePluginStream::WebNetscapePluginStream(NSURLRequest *request, NPP plugin, bool sendNotification, void* notifyData) + : m_requestURL([request URL]) + , m_plugin(0) + , m_transferMode(0) + , m_offset(0) + , m_fileDescriptor(-1) + , m_sendNotification(sendNotification) + , m_notifyData(notifyData) + , m_headers(0) + , m_reason(NPRES_BASE) + , m_isTerminated(false) + , m_newStreamSuccessful(false) + , m_frameLoader(0) + , m_request(AdoptNS, [request mutableCopy]) + , m_pluginFuncs(0) + , m_deliverDataTimer(this, &WebNetscapePluginStream::deliverDataTimerFired) +{ + memset(&m_stream, 0, sizeof(NPStream)); + + WebNetscapePluginView *view = (WebNetscapePluginView *)plugin->ndata; + + // This check has already been done by the plug-in view. + ASSERT(core([view webFrame])->document()->securityOrigin()->canDisplay([request URL])); + + ASSERT([request URL]); + ASSERT(plugin); + + setPlugin(plugin); + + streams().add(&m_stream, plugin); + + if (SecurityOrigin::shouldHideReferrer([request URL], core([view webFrame])->loader()->outgoingReferrer())) + [m_request.get() _web_setHTTPReferrer:nil]; +} + +WebNetscapePluginStream::~WebNetscapePluginStream() +{ + ASSERT(!m_plugin); + ASSERT(m_isTerminated); + ASSERT(!m_stream.ndata); + + // The stream file should have been deleted, and the path freed, in -_destroyStream + ASSERT(!m_path); + ASSERT(m_fileDescriptor == -1); + + free((void *)m_stream.url); + free(m_headers); + + streams().remove(&m_stream); +} + +void WebNetscapePluginStream::setPlugin(NPP plugin) +{ + if (plugin) { + m_plugin = plugin; + m_pluginView = static_cast<WebNetscapePluginView *>(m_plugin->ndata); + + WebNetscapePluginPackage *pluginPackage = [m_pluginView.get() pluginPackage]; + + m_pluginFuncs = [pluginPackage pluginFuncs]; + } else { + WebNetscapePluginView *view = m_pluginView.get(); + m_plugin = 0; + m_pluginFuncs = 0; + + [view disconnectStream:this]; + m_pluginView = 0; + } +} + +void WebNetscapePluginStream::startStream(NSURL *url, long long expectedContentLength, NSDate *lastModifiedDate, const String& mimeType, NSData *headers) +{ + ASSERT(!m_isTerminated); + + m_responseURL = url; + m_mimeType = mimeType.utf8(); + + free((void *)m_stream.url); + m_stream.url = strdup([m_responseURL.get() _web_URLCString]); + + m_stream.ndata = this; + m_stream.end = expectedContentLength > 0 ? (uint32_t)expectedContentLength : 0; + m_stream.lastmodified = (uint32_t)[lastModifiedDate timeIntervalSince1970]; + m_stream.notifyData = m_notifyData; + + if (headers) { + unsigned len = [headers length]; + m_headers = (char*) malloc(len + 1); + [headers getBytes:m_headers]; + m_headers[len] = 0; + m_stream.headers = m_headers; + } + + m_transferMode = NP_NORMAL; + m_offset = 0; + m_reason = WEB_REASON_NONE; + // FIXME: If WebNetscapePluginStream called our initializer we wouldn't have to do this here. + m_fileDescriptor = -1; + + // FIXME: Need a way to check if stream is seekable + + NPError npErr; + { + PluginStopDeferrer deferrer(m_pluginView.get()); + npErr = m_pluginFuncs->newstream(m_plugin, m_mimeType.mutableData(), &m_stream, NO, &m_transferMode); + } + + LOG(Plugins, "NPP_NewStream URL=%@ MIME=%s error=%d", m_responseURL.get(), m_mimeType.data(), npErr); + + if (npErr != NPERR_NO_ERROR) { + LOG_ERROR("NPP_NewStream failed with error: %d responseURL: %@", npErr, m_responseURL.get()); + // Calling cancelLoadWithError: cancels the load, but doesn't call NPP_DestroyStream. + cancelLoadWithError(pluginCancelledConnectionError()); + return; + } + + m_newStreamSuccessful = true; + + switch (m_transferMode) { + case NP_NORMAL: + LOG(Plugins, "Stream type: NP_NORMAL"); + break; + case NP_ASFILEONLY: + LOG(Plugins, "Stream type: NP_ASFILEONLY"); + break; + case NP_ASFILE: + LOG(Plugins, "Stream type: NP_ASFILE"); + break; + case NP_SEEK: + LOG_ERROR("Stream type: NP_SEEK not yet supported"); + cancelLoadAndDestroyStreamWithError(pluginCancelledConnectionError()); + break; + default: + LOG_ERROR("unknown stream type"); + } +} + +void WebNetscapePluginStream::start() +{ + ASSERT(m_request); + ASSERT(!m_frameLoader); + ASSERT(!m_loader); + + m_loader = resourceLoadScheduler()->schedulePluginStreamLoad(core([m_pluginView.get() webFrame]), this, m_request.get()); +} + +void WebNetscapePluginStream::stop() +{ + ASSERT(!m_frameLoader); + + if (!m_loader->isDone()) + cancelLoadAndDestroyStreamWithError(m_loader->cancelledError()); +} + +void WebNetscapePluginStream::didReceiveResponse(NetscapePlugInStreamLoader*, const ResourceResponse& response) +{ + NSURLResponse *r = response.nsURLResponse(); + + NSMutableData *theHeaders = nil; + long long expectedContentLength = [r expectedContentLength]; + + if ([r isKindOfClass:[NSHTTPURLResponse class]]) { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)r; + theHeaders = [NSMutableData dataWithCapacity:1024]; + + // FIXME: it would be nice to be able to get the raw HTTP header block. + // This includes the HTTP version, the real status text, + // all headers in their original order and including duplicates, + // and all original bytes verbatim, rather than sent through Unicode translation. + // Unfortunately NSHTTPURLResponse doesn't provide access at that low a level. + + [theHeaders appendBytes:"HTTP " length:5]; + char statusStr[10]; + long statusCode = [httpResponse statusCode]; + snprintf(statusStr, sizeof(statusStr), "%ld", statusCode); + [theHeaders appendBytes:statusStr length:strlen(statusStr)]; + [theHeaders appendBytes:" OK\n" length:4]; + + // HACK: pass the headers through as UTF-8. + // This is not the intended behavior; we're supposed to pass original bytes verbatim. + // But we don't have the original bytes, we have NSStrings built by the URL loading system. + // It hopefully shouldn't matter, since RFC2616/RFC822 require ASCII-only headers, + // but surely someone out there is using non-ASCII characters, and hopefully UTF-8 is adequate here. + // It seems better than NSASCIIStringEncoding, which will lose information if non-ASCII is used. + + NSDictionary *headerDict = [httpResponse allHeaderFields]; + NSArray *keys = [[headerDict allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; + NSEnumerator *i = [keys objectEnumerator]; + NSString *k; + while ((k = [i nextObject]) != nil) { + NSString *v = [headerDict objectForKey:k]; + [theHeaders appendData:[k dataUsingEncoding:NSUTF8StringEncoding]]; + [theHeaders appendBytes:": " length:2]; + [theHeaders appendData:[v dataUsingEncoding:NSUTF8StringEncoding]]; + [theHeaders appendBytes:"\n" length:1]; + } + + // If the content is encoded (most likely compressed), then don't send its length to the plugin, + // which is only interested in the decoded length, not yet known at the moment. + // <rdar://problem/4470599> tracks a request for -[NSURLResponse expectedContentLength] to incorporate this logic. + NSString *contentEncoding = (NSString *)[[(NSHTTPURLResponse *)r allHeaderFields] objectForKey:@"Content-Encoding"]; + if (contentEncoding && ![contentEncoding isEqualToString:@"identity"]) + expectedContentLength = -1; + + // startStreamResponseURL:... will null-terminate. + } + + startStream([r URL], expectedContentLength, WKGetNSURLResponseLastModifiedDate(r), response.mimeType(), theHeaders); +} + +void WebNetscapePluginStream::startStreamWithResponse(NSURLResponse *response) +{ + didReceiveResponse(0, response); +} + +bool WebNetscapePluginStream::wantsAllStreams() const +{ + if (!m_pluginFuncs->getvalue) + return false; + + void *value = 0; + NPError error; + { + PluginStopDeferrer deferrer(m_pluginView.get()); + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + error = m_pluginFuncs->getvalue(m_plugin, NPPVpluginWantsAllNetworkStreams, &value); + } + if (error != NPERR_NO_ERROR) + return false; + + return value; +} + +void WebNetscapePluginStream::destroyStream() +{ + if (m_isTerminated) + return; + + RefPtr<WebNetscapePluginStream> protect(this); + + ASSERT(m_reason != WEB_REASON_NONE); + ASSERT([m_deliveryData.get() length] == 0); + + m_deliverDataTimer.stop(); + + if (m_stream.ndata) { + if (m_reason == NPRES_DONE && (m_transferMode == NP_ASFILE || m_transferMode == NP_ASFILEONLY)) { + ASSERT(m_fileDescriptor == -1); + ASSERT(m_path); + NSString *carbonPath = CarbonPathFromPOSIXPath(m_path.get()); + ASSERT(carbonPath != NULL); + + PluginStopDeferrer deferrer(m_pluginView.get()); + m_pluginFuncs->asfile(m_plugin, &m_stream, [carbonPath fileSystemRepresentation]); + LOG(Plugins, "NPP_StreamAsFile responseURL=%@ path=%s", m_responseURL.get(), carbonPath); + } + + if (m_path) { + // Delete the file after calling NPP_StreamAsFile(), instead of in -dealloc/-finalize. It should be OK + // to delete the file here -- NPP_StreamAsFile() is always called immediately before NPP_DestroyStream() + // (the stream destruction function), so there can be no expectation that a plugin will read the stream + // file asynchronously after NPP_StreamAsFile() is called. + unlink([m_path.get() fileSystemRepresentation]); + m_path = 0; + + if (m_isTerminated) + return; + } + + if (m_fileDescriptor != -1) { + // The file may still be open if we are destroying the stream before it completed loading. + close(m_fileDescriptor); + m_fileDescriptor = -1; + } + + if (m_newStreamSuccessful) { + PluginStopDeferrer deferrer(m_pluginView.get()); +#if !LOG_DISABLED + NPError npErr = +#endif + m_pluginFuncs->destroystream(m_plugin, &m_stream, m_reason); + LOG(Plugins, "NPP_DestroyStream responseURL=%@ error=%d", m_responseURL.get(), npErr); + } + + free(m_headers); + m_headers = NULL; + m_stream.headers = NULL; + + m_stream.ndata = 0; + + if (m_isTerminated) + return; + } + + if (m_sendNotification) { + // NPP_URLNotify expects the request URL, not the response URL. + PluginStopDeferrer deferrer(m_pluginView.get()); + m_pluginFuncs->urlnotify(m_plugin, m_requestURL.string().utf8().data(), m_reason, m_notifyData); + LOG(Plugins, "NPP_URLNotify requestURL=%@ reason=%d", (NSURL *)m_requestURL, m_reason); + } + + m_isTerminated = true; + + setPlugin(0); +} + +void WebNetscapePluginStream::destroyStreamWithReason(NPReason reason) +{ + m_reason = reason; + if (m_reason != NPRES_DONE) { + // Stop any pending data from being streamed. + [m_deliveryData.get() setLength:0]; + } else if ([m_deliveryData.get() length] > 0) { + // There is more data to be streamed, don't destroy the stream now. + return; + } + + RefPtr<WebNetscapePluginStream> protect(this); + destroyStream(); + ASSERT(!m_stream.ndata); +} + +void WebNetscapePluginStream::cancelLoadWithError(NSError *error) +{ + if (m_frameLoader) { + ASSERT(!m_loader); + + DocumentLoader* documentLoader = m_frameLoader->activeDocumentLoader(); + ASSERT(documentLoader); + + if (documentLoader->isLoadingMainResource()) + documentLoader->cancelMainResourceLoad(error); + return; + } + + if (!m_loader->isDone()) + m_loader->cancel(error); +} + +void WebNetscapePluginStream::destroyStreamWithError(NSError *error) +{ + destroyStreamWithReason(reasonForError(error)); +} + +void WebNetscapePluginStream::didFail(WebCore::NetscapePlugInStreamLoader*, const WebCore::ResourceError& error) +{ + destroyStreamWithError(error); +} + +void WebNetscapePluginStream::cancelLoadAndDestroyStreamWithError(NSError *error) +{ + RefPtr<WebNetscapePluginStream> protect(this); + cancelLoadWithError(error); + destroyStreamWithError(error); + setPlugin(0); +} + +void WebNetscapePluginStream::deliverData() +{ + if (!m_stream.ndata || [m_deliveryData.get() length] == 0) + return; + + RefPtr<WebNetscapePluginStream> protect(this); + + int32_t totalBytes = [m_deliveryData.get() length]; + int32_t totalBytesDelivered = 0; + + while (totalBytesDelivered < totalBytes) { + PluginStopDeferrer deferrer(m_pluginView.get()); + int32_t deliveryBytes = m_pluginFuncs->writeready(m_plugin, &m_stream); + LOG(Plugins, "NPP_WriteReady responseURL=%@ bytes=%d", m_responseURL.get(), deliveryBytes); + + if (m_isTerminated) + return; + + if (deliveryBytes <= 0) { + // Plug-in can't receive anymore data right now. Send it later. + if (!m_deliverDataTimer.isActive()) + m_deliverDataTimer.startOneShot(0); + break; + } else { + deliveryBytes = min(deliveryBytes, totalBytes - totalBytesDelivered); + NSData *subdata = [m_deliveryData.get() subdataWithRange:NSMakeRange(totalBytesDelivered, deliveryBytes)]; + PluginStopDeferrer deferrer(m_pluginView.get()); + deliveryBytes = m_pluginFuncs->write(m_plugin, &m_stream, m_offset, [subdata length], (void *)[subdata bytes]); + if (deliveryBytes < 0) { + // Netscape documentation says that a negative result from NPP_Write means cancel the load. + cancelLoadAndDestroyStreamWithError(pluginCancelledConnectionError()); + return; + } + deliveryBytes = min<int32_t>(deliveryBytes, [subdata length]); + m_offset += deliveryBytes; + totalBytesDelivered += deliveryBytes; + LOG(Plugins, "NPP_Write responseURL=%@ bytes=%d total-delivered=%d/%d", m_responseURL.get(), deliveryBytes, m_offset, m_stream.end); + } + } + + if (totalBytesDelivered > 0) { + if (totalBytesDelivered < totalBytes) { + NSMutableData *newDeliveryData = [[NSMutableData alloc] initWithCapacity:totalBytes - totalBytesDelivered]; + [newDeliveryData appendBytes:(char *)[m_deliveryData.get() bytes] + totalBytesDelivered length:totalBytes - totalBytesDelivered]; + + m_deliveryData.adoptNS(newDeliveryData); + } else { + [m_deliveryData.get() setLength:0]; + if (m_reason != WEB_REASON_NONE) + destroyStream(); + } + } +} + +void WebNetscapePluginStream::deliverDataTimerFired(WebCore::Timer<WebNetscapePluginStream>* timer) +{ + deliverData(); +} + +void WebNetscapePluginStream::deliverDataToFile(NSData *data) +{ + if (m_fileDescriptor == -1 && !m_path) { + NSString *temporaryFileMask = [NSTemporaryDirectory() stringByAppendingPathComponent:@"WebKitPlugInStreamXXXXXX"]; + char *temporaryFileName = strdup([temporaryFileMask fileSystemRepresentation]); + m_fileDescriptor = mkstemp(temporaryFileName); + if (m_fileDescriptor == -1) { + LOG_ERROR("Can't create a temporary file."); + // This is not a network error, but the only error codes are "network error" and "user break". + destroyStreamWithReason(NPRES_NETWORK_ERR); + free(temporaryFileName); + return; + } + + m_path.adoptNS([[NSString stringWithUTF8String:temporaryFileName] retain]); + free(temporaryFileName); + } + + int dataLength = [data length]; + if (!dataLength) + return; + + int byteCount = write(m_fileDescriptor, [data bytes], dataLength); + if (byteCount != dataLength) { + // This happens only rarely, when we are out of disk space or have a disk I/O error. + LOG_ERROR("error writing to temporary file, errno %d", errno); + close(m_fileDescriptor); + m_fileDescriptor = -1; + + // This is not a network error, but the only error codes are "network error" and "user break". + destroyStreamWithReason(NPRES_NETWORK_ERR); + m_path = 0; + } +} + +void WebNetscapePluginStream::didFinishLoading(NetscapePlugInStreamLoader*) +{ + if (!m_stream.ndata) + return; + + if (m_transferMode == NP_ASFILE || m_transferMode == NP_ASFILEONLY) { + // Fake the delivery of an empty data to ensure that the file has been created + deliverDataToFile([NSData data]); + if (m_fileDescriptor != -1) + close(m_fileDescriptor); + m_fileDescriptor = -1; + } + + destroyStreamWithReason(NPRES_DONE); +} + +void WebNetscapePluginStream::didReceiveData(NetscapePlugInStreamLoader*, const char* bytes, int length) +{ + NSData *data = [[NSData alloc] initWithBytesNoCopy:(void*)bytes length:length freeWhenDone:NO]; + + ASSERT([data length] > 0); + + if (m_transferMode != NP_ASFILEONLY) { + if (!m_deliveryData) + m_deliveryData.adoptNS([[NSMutableData alloc] initWithCapacity:[data length]]); + [m_deliveryData.get() appendData:data]; + deliverData(); + } + if (m_transferMode == NP_ASFILE || m_transferMode == NP_ASFILEONLY) + deliverDataToFile(data); + + [data release]; +} + +static NSString *CarbonPathFromPOSIXPath(NSString *posixPath) +{ + // Doesn't add a trailing colon for directories; this is a problem for paths to a volume, + // so this function would need to be revised if we ever wanted to call it with that. + + CFURLRef url = (CFURLRef)[NSURL fileURLWithPath:posixPath]; + if (!url) + return nil; + + return WebCFAutorelease(CFURLCopyFileSystemPath(url, kCFURLHFSPathStyle)); +} + +#endif diff --git a/Source/WebKit/mac/Plugins/WebNetscapePluginView.h b/Source/WebKit/mac/Plugins/WebNetscapePluginView.h new file mode 100644 index 0000000..b2debfa --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebNetscapePluginView.h @@ -0,0 +1,189 @@ +/* + * Copyright (C) 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. + */ + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#import "WebBaseNetscapePluginView.h" + +#import "WebNetscapeContainerCheckPrivate.h" +#import <WebKit/npfunctions.h> +#import <WebKit/npapi.h> +#import <wtf/HashMap.h> +#import <wtf/HashSet.h> +#import <wtf/OwnPtr.h> + +@class WebDataSource; +@class WebFrame; +@class WebNetscapePluginPackage; +@class WebView; + +class PluginTimer; +class WebNetscapePluginStream; +class WebNetscapePluginEventHandler; + +typedef union PluginPort { +#ifndef NP_NO_QUICKDRAW + NP_Port qdPort; +#endif + NP_CGContext cgPort; +} PluginPort; + +// Because the Adobe 7.x Acrobat plug-in has a hard coded check for a view named +// "WebNetscapePluginDocumentView", this class must retain the old name in order +// for the plug-in to function correctly. (rdar://problem/4699455) +#define WebNetscapePluginView WebNetscapePluginDocumentView + +@interface WebNetscapePluginView : WebBaseNetscapePluginView<WebPluginManualLoader, WebPluginContainerCheckController> +{ + RefPtr<WebNetscapePluginStream> _manualStream; +#ifndef BUILDING_ON_TIGER + RetainPtr<CALayer> _pluginLayer; +#endif + unsigned _dataLengthReceived; + RetainPtr<NSError> _error; + + unsigned argsCount; + char **cAttributes; + char **cValues; + + NPP plugin; + NPWindow window; + NPWindow lastSetWindow; + PluginPort nPort; + PluginPort lastSetPort; + NPDrawingModel drawingModel; + NPEventModel eventModel; + +#ifndef NP_NO_QUICKDRAW + // This is only valid when drawingModel is NPDrawingModelQuickDraw + GWorldPtr offscreenGWorld; +#endif + + OwnPtr<WebNetscapePluginEventHandler> _eventHandler; + + BOOL inSetWindow; + BOOL shouldStopSoon; + + uint32_t currentTimerID; + HashMap<uint32_t, PluginTimer*>* timers; + + unsigned pluginFunctionCallDepth; + + int32_t specifiedHeight; + int32_t specifiedWidth; + + HashSet<RefPtr<WebNetscapePluginStream> > streams; + RetainPtr<NSMutableDictionary> _pendingFrameLoads; + + BOOL _isFlash; + BOOL _isSilverlight; + + NSMutableDictionary *_containerChecksInProgress; + uint32_t _currentContainerCheckRequestID; +} + ++ (WebNetscapePluginView *)currentPluginView; + + +- (id)initWithFrame:(NSRect)r + pluginPackage:(WebNetscapePluginPackage *)thePluginPackage + URL:(NSURL *)URL + baseURL:(NSURL *)baseURL + MIMEType:(NSString *)MIME + attributeKeys:(NSArray *)keys + attributeValues:(NSArray *)values + loadManually:(BOOL)loadManually + element:(PassRefPtr<WebCore::HTMLPlugInElement>)element; + + +- (NPP)plugin; + +- (void)disconnectStream:(WebNetscapePluginStream*)stream; + +// Returns the NPObject that represents the plugin interface. +// The return value is expected to be retained. +- (NPObject *)createPluginScriptableObject; + +// -willCallPlugInFunction must be called before calling any of the NPP_* functions for this view's plugin. +// This is necessary to ensure that plug-ins are not destroyed while WebKit calls into them. Some plug-ins (Flash +// at least) are written with the assumption that nothing they do in their plug-in functions can cause NPP_Destroy() +// to be called. Unfortunately, this is not true, especially if the plug-in uses NPN_Invoke() to execute a +// document.write(), which clears the document and destroys the plug-in. +// See <rdar://problem/4480737>. +- (void)willCallPlugInFunction; + +// -didCallPlugInFunction should be called after returning from a plug-in function. It should be called exactly +// once for every call to -willCallPlugInFunction. +// See <rdar://problem/4480737>. +- (void)didCallPlugInFunction; + +- (void)handleMouseMoved:(NSEvent *)event; +- (void)handleMouseEntered:(NSEvent *)event; +- (void)handleMouseExited:(NSEvent *)event; + +- (uint32_t)checkIfAllowedToLoadURL:(const char*)urlCString frame:(const char*)frameNameCString callbackFunc:(void (*)(NPP npp, uint32_t checkID, NPBool allowed, void* context))callbackFunc context:(void*)context; +- (void)cancelCheckIfAllowedToLoadURL:(uint32_t)checkID; + +@end + +@interface WebNetscapePluginView (WebInternal) +- (BOOL)sendEvent:(void*)event isDrawRect:(BOOL)eventIsDrawRect; +- (NPEventModel)eventModel; +#ifndef BUILDING_ON_TIGER +- (CALayer *)pluginLayer; +#endif +- (NPError)loadRequest:(NSURLRequest *)request inTarget:(NSString *)target withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification; +- (NPError)getURLNotify:(const char *)URL target:(const char *)target notifyData:(void *)notifyData; +- (NPError)getURL:(const char *)URL target:(const char *)target; +- (NPError)postURLNotify:(const char *)URL target:(const char *)target len:(UInt32)len buf:(const char *)buf file:(NPBool)file notifyData:(void *)notifyData; +- (NPError)postURL:(const char *)URL target:(const char *)target len:(UInt32)len buf:(const char *)buf file:(NPBool)file; +- (NPError)newStream:(NPMIMEType)type target:(const char *)target stream:(NPStream**)stream; +- (NPError)write:(NPStream*)stream len:(SInt32)len buffer:(void *)buffer; +- (NPError)destroyStream:(NPStream*)stream reason:(NPReason)reason; +- (void)status:(const char *)message; +- (const char *)userAgent; +- (void)invalidateRect:(NPRect *)invalidRect; +- (void)invalidateRegion:(NPRegion)invalidateRegion; +- (void)forceRedraw; +- (NPError)getVariable:(NPNVariable)variable value:(void *)value; +- (NPError)setVariable:(NPPVariable)variable value:(void *)value; +- (uint32_t)scheduleTimerWithInterval:(uint32_t)interval repeat:(NPBool)repeat timerFunc:(void (*)(NPP npp, uint32_t timerID))timerFunc; +- (void)unscheduleTimer:(uint32_t)timerID; +- (NPError)popUpContextMenu:(NPMenu *)menu; +- (NPError)getVariable:(NPNURLVariable)variable forURL:(const char*)url value:(char**)value length:(uint32_t*)length; +- (NPError)setVariable:(NPNURLVariable)variable forURL:(const char*)url value:(const char*)value length:(uint32_t)length; +- (NPError)getAuthenticationInfoWithProtocol:(const char*) protocol host:(const char*)host port:(int32_t)port scheme:(const char*)scheme realm:(const char*)realm + username:(char**)username usernameLength:(uint32_t*)usernameLength + password:(char**)password passwordLength:(uint32_t*)passwordLength; +- (char*)resolveURL:(const char*)url forTarget:(const char*)target; +@end + +WKNBrowserContainerCheckFuncs *browserContainerCheckFuncs(); + +#endif + diff --git a/Source/WebKit/mac/Plugins/WebNetscapePluginView.mm b/Source/WebKit/mac/Plugins/WebNetscapePluginView.mm new file mode 100644 index 0000000..f9f41e9 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebNetscapePluginView.mm @@ -0,0 +1,2513 @@ +/* + * 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. + */ + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#import "WebNetscapePluginView.h" + +#import "QuickDrawCompatibility.h" +#import "WebDataSourceInternal.h" +#import "WebDefaultUIDelegate.h" +#import "WebFrameInternal.h" +#import "WebFrameView.h" +#import "WebKitErrorsPrivate.h" +#import "WebKitLogging.h" +#import "WebKitNSStringExtras.h" +#import "WebKitSystemInterface.h" +#import "WebNSDataExtras.h" +#import "WebNSDictionaryExtras.h" +#import "WebNSObjectExtras.h" +#import "WebNSURLExtras.h" +#import "WebNSURLRequestExtras.h" +#import "WebNSViewExtras.h" +#import "WebNetscapeContainerCheckContextInfo.h" +#import "WebNetscapeContainerCheckPrivate.h" +#import "WebNetscapePluginEventHandler.h" +#import "WebNetscapePluginPackage.h" +#import "WebNetscapePluginStream.h" +#import "WebPluginContainerCheck.h" +#import "WebPluginRequest.h" +#import "WebPreferences.h" +#import "WebUIDelegatePrivate.h" +#import "WebViewInternal.h" +#import <Carbon/Carbon.h> +#import <WebCore/CookieJar.h> +#import <WebCore/DocumentLoader.h> +#import <WebCore/Element.h> +#import <WebCore/Frame.h> +#import <WebCore/FrameLoader.h> +#import <WebCore/FrameTree.h> +#import <WebCore/FrameView.h> +#import <WebCore/HTMLPlugInElement.h> +#import <WebCore/Page.h> +#import <WebCore/PluginMainThreadScheduler.h> +#import <WebCore/ProxyServer.h> +#import <WebCore/ScriptController.h> +#import <WebCore/SecurityOrigin.h> +#import <WebCore/SoftLinking.h> +#import <WebCore/WebCoreObjCExtras.h> +#import <WebCore/WebCoreURLResponse.h> +#import <WebCore/npruntime_impl.h> +#import <WebKit/DOMPrivate.h> +#import <WebKit/WebUIDelegate.h> +#import <objc/objc-runtime.h> +#import <runtime/InitializeThreading.h> +#import <runtime/JSLock.h> +#import <wtf/Assertions.h> +#import <wtf/Threading.h> +#import <wtf/text/CString.h> + +#define LoginWindowDidSwitchFromUserNotification @"WebLoginWindowDidSwitchFromUserNotification" +#define LoginWindowDidSwitchToUserNotification @"WebLoginWindowDidSwitchToUserNotification" +#define WKNVSupportsCompositingCoreAnimationPluginsBool 74656 /* TRUE if the browser supports hardware compositing of Core Animation plug-ins */ +static const int WKNVSilverlightFullscreenPerformanceIssueFixed = 7288546; /* TRUE if Siverlight addressed its underlying bug in <rdar://problem/7288546> */ + +using namespace WebCore; +using namespace WebKit; +using namespace std; + +static inline bool isDrawingModelQuickDraw(NPDrawingModel drawingModel) +{ +#ifndef NP_NO_QUICKDRAW + return drawingModel == NPDrawingModelQuickDraw; +#else + return false; +#endif +}; + +@interface WebNetscapePluginView (Internal) +- (NPError)_createPlugin; +- (void)_destroyPlugin; +- (NSBitmapImageRep *)_printedPluginBitmap; +- (void)_redeliverStream; +- (BOOL)_shouldCancelSrcStream; +@end + +static WebNetscapePluginView *currentPluginView = nil; + +typedef struct OpaquePortState* PortState; + +static const double ThrottledTimerInterval = 0.25; + +class PluginTimer : public TimerBase { +public: + typedef void (*TimerFunc)(NPP npp, uint32_t timerID); + + PluginTimer(NPP npp, uint32_t timerID, uint32_t interval, NPBool repeat, TimerFunc timerFunc) + : m_npp(npp) + , m_timerID(timerID) + , m_interval(interval) + , m_repeat(repeat) + , m_timerFunc(timerFunc) + { + } + + void start(bool throttle) + { + ASSERT(!isActive()); + + double timeInterval = m_interval / 1000.0; + + if (throttle) + timeInterval = max(timeInterval, ThrottledTimerInterval); + + if (m_repeat) + startRepeating(timeInterval); + else + startOneShot(timeInterval); + } + +private: + virtual void fired() + { + m_timerFunc(m_npp, m_timerID); + if (!m_repeat) + delete this; + } + + NPP m_npp; + uint32_t m_timerID; + uint32_t m_interval; + NPBool m_repeat; + TimerFunc m_timerFunc; +}; + +#ifndef NP_NO_QUICKDRAW + +// QuickDraw is not available in 64-bit + +typedef struct { + GrafPtr oldPort; + GDHandle oldDevice; + Point oldOrigin; + RgnHandle oldClipRegion; + RgnHandle oldVisibleRegion; + RgnHandle clipRegion; + BOOL forUpdate; +} PortState_QD; + +#endif /* NP_NO_QUICKDRAW */ + +typedef struct { + CGContextRef context; +} PortState_CG; + +@class NSTextInputContext; +@interface NSResponder (AppKitDetails) +- (NSTextInputContext *)inputContext; +@end + +@interface WebNetscapePluginView (ForwardDeclarations) +- (void)setWindowIfNecessary; +- (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification; +@end + +@implementation WebNetscapePluginView + ++ (void)initialize +{ + JSC::initializeThreading(); + WTF::initializeMainThreadToProcessMainThread(); +#ifndef BUILDING_ON_TIGER + WebCoreObjCFinalizeOnMainThread(self); +#endif + WKSendUserChangeNotifications(); +} + +// MARK: EVENTS + +// The WindowRef created by -[NSWindow windowRef] has a QuickDraw GrafPort that covers +// the entire window frame (or structure region to use the Carbon term) rather then just the window content. +// We can remove this when <rdar://problem/4201099> is fixed. +- (void)fixWindowPort +{ +#ifndef NP_NO_QUICKDRAW + ASSERT(isDrawingModelQuickDraw(drawingModel)); + + NSWindow *currentWindow = [self currentWindow]; + if ([currentWindow isKindOfClass:objc_getClass("NSCarbonWindow")]) + return; + + float windowHeight = [currentWindow frame].size.height; + NSView *contentView = [currentWindow contentView]; + NSRect contentRect = [contentView convertRect:[contentView frame] toView:nil]; // convert to window-relative coordinates + + CGrafPtr oldPort; + GetPort(&oldPort); + SetPort(GetWindowPort((WindowRef)[currentWindow windowRef])); + + MovePortTo(static_cast<short>(contentRect.origin.x), /* Flip Y */ static_cast<short>(windowHeight - NSMaxY(contentRect))); + PortSize(static_cast<short>(contentRect.size.width), static_cast<short>(contentRect.size.height)); + + SetPort(oldPort); +#endif +} + +#ifndef NP_NO_QUICKDRAW +static UInt32 getQDPixelFormatForBitmapContext(CGContextRef context) +{ + UInt32 byteOrder = CGBitmapContextGetBitmapInfo(context) & kCGBitmapByteOrderMask; + if (byteOrder == kCGBitmapByteOrderDefault) + switch (CGBitmapContextGetBitsPerPixel(context)) { + case 16: + byteOrder = kCGBitmapByteOrder16Host; + break; + case 32: + byteOrder = kCGBitmapByteOrder32Host; + break; + } + switch (byteOrder) { + case kCGBitmapByteOrder16Little: + return k16LE555PixelFormat; + case kCGBitmapByteOrder32Little: + return k32BGRAPixelFormat; + case kCGBitmapByteOrder16Big: + return k16BE555PixelFormat; + case kCGBitmapByteOrder32Big: + return k32ARGBPixelFormat; + } + ASSERT_NOT_REACHED(); + return 0; +} + +static inline void getNPRect(const CGRect& cgr, NPRect& npr) +{ + npr.top = static_cast<uint16_t>(cgr.origin.y); + npr.left = static_cast<uint16_t>(cgr.origin.x); + npr.bottom = static_cast<uint16_t>(CGRectGetMaxY(cgr)); + npr.right = static_cast<uint16_t>(CGRectGetMaxX(cgr)); +} + +#endif + +static inline void getNPRect(const NSRect& nr, NPRect& npr) +{ + npr.top = static_cast<uint16_t>(nr.origin.y); + npr.left = static_cast<uint16_t>(nr.origin.x); + npr.bottom = static_cast<uint16_t>(NSMaxY(nr)); + npr.right = static_cast<uint16_t>(NSMaxX(nr)); +} + +- (PortState)saveAndSetNewPortStateForUpdate:(BOOL)forUpdate +{ + ASSERT([self currentWindow] != nil); + + // The base coordinates of a window and it's contentView happen to be the equal at a userSpaceScaleFactor + // of 1. For non-1.0 scale factors this assumption is false. + NSView *windowContentView = [[self window] contentView]; + NSRect boundsInWindow = [self convertRect:[self bounds] toView:windowContentView]; + NSRect visibleRectInWindow = [self actualVisibleRectInWindow]; + + // Flip Y to convert -[NSWindow contentView] coordinates to top-left-based window coordinates. + float borderViewHeight = [[self currentWindow] frame].size.height; + boundsInWindow.origin.y = borderViewHeight - NSMaxY(boundsInWindow); + visibleRectInWindow.origin.y = borderViewHeight - NSMaxY(visibleRectInWindow); + +#ifndef NP_NO_QUICKDRAW + WindowRef windowRef = (WindowRef)[[self currentWindow] windowRef]; + ASSERT(windowRef); + + // Look at the Carbon port to convert top-left-based window coordinates into top-left-based content coordinates. + if (isDrawingModelQuickDraw(drawingModel)) { + // If drawing with QuickDraw, fix the window port so that it has the same bounds as the NSWindow's + // content view. This makes it easier to convert between AppKit view and QuickDraw port coordinates. + [self fixWindowPort]; + + ::Rect portBounds; + CGrafPtr port = GetWindowPort(windowRef); + GetPortBounds(port, &portBounds); + + PixMap *pix = *GetPortPixMap(port); + boundsInWindow.origin.x += pix->bounds.left - portBounds.left; + boundsInWindow.origin.y += pix->bounds.top - portBounds.top; + visibleRectInWindow.origin.x += pix->bounds.left - portBounds.left; + visibleRectInWindow.origin.y += pix->bounds.top - portBounds.top; + } +#endif + + window.type = NPWindowTypeWindow; + window.x = (int32_t)boundsInWindow.origin.x; + window.y = (int32_t)boundsInWindow.origin.y; + window.width = static_cast<uint32_t>(NSWidth(boundsInWindow)); + window.height = static_cast<uint32_t>(NSHeight(boundsInWindow)); + + // "Clip-out" the plug-in when: + // 1) it's not really in a window or off-screen or has no height or width. + // 2) window.x is a "big negative number" which is how WebCore expresses off-screen widgets. + // 3) the window is miniaturized or the app is hidden + // 4) we're inside of viewWillMoveToWindow: with a nil window. In this case, superviews may already have nil + // superviews and nil windows and results from convertRect:toView: are incorrect. + if (window.width <= 0 || window.height <= 0 || window.x < -100000 || [self shouldClipOutPlugin]) { + + // The following code tries to give plug-ins the same size they will eventually have. + // The specifiedWidth and specifiedHeight variables are used to predict the size that + // WebCore will eventually resize us to. + + // The QuickTime plug-in has problems if you give it a width or height of 0. + // Since other plug-ins also might have the same sort of trouble, we make sure + // to always give plug-ins a size other than 0,0. + + if (window.width <= 0) + window.width = specifiedWidth > 0 ? specifiedWidth : 100; + if (window.height <= 0) + window.height = specifiedHeight > 0 ? specifiedHeight : 100; + + window.clipRect.bottom = window.clipRect.top; + window.clipRect.left = window.clipRect.right; + + // Core Animation plug-ins need to be updated (with a 0,0,0,0 clipRect) when + // moved to a background tab. We don't do this for Core Graphics plug-ins as + // older versions of Flash have historical WebKit-specific code that isn't + // compatible with this behavior. + if (drawingModel == NPDrawingModelCoreAnimation) + getNPRect(NSZeroRect, window.clipRect); + } else { + getNPRect(visibleRectInWindow, window.clipRect); + } + + // Save the port state, set up the port for entry into the plugin + PortState portState; + switch (drawingModel) { +#ifndef NP_NO_QUICKDRAW + case NPDrawingModelQuickDraw: { + // Set up NS_Port. + ::Rect portBounds; + CGrafPtr port = GetWindowPort(windowRef); + GetPortBounds(port, &portBounds); + nPort.qdPort.port = port; + nPort.qdPort.portx = (int32_t)-boundsInWindow.origin.x; + nPort.qdPort.porty = (int32_t)-boundsInWindow.origin.y; + window.window = &nPort; + + PortState_QD *qdPortState = (PortState_QD*)malloc(sizeof(PortState_QD)); + portState = (PortState)qdPortState; + + GetGWorld(&qdPortState->oldPort, &qdPortState->oldDevice); + + qdPortState->oldOrigin.h = portBounds.left; + qdPortState->oldOrigin.v = portBounds.top; + + qdPortState->oldClipRegion = NewRgn(); + GetPortClipRegion(port, qdPortState->oldClipRegion); + + qdPortState->oldVisibleRegion = NewRgn(); + GetPortVisibleRegion(port, qdPortState->oldVisibleRegion); + + RgnHandle clipRegion = NewRgn(); + qdPortState->clipRegion = clipRegion; + + CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; + if (currentContext && WKCGContextIsBitmapContext(currentContext)) { + // We use WKCGContextIsBitmapContext here, because if we just called CGBitmapContextGetData + // on any context, we'd log to the console every time. But even if WKCGContextIsBitmapContext + // returns true, it still might not be a context we need to create a GWorld for; for example + // transparency layers will return true, but return 0 for CGBitmapContextGetData. + void* offscreenData = CGBitmapContextGetData(currentContext); + if (offscreenData) { + // If the current context is an offscreen bitmap, then create a GWorld for it. + ::Rect offscreenBounds; + offscreenBounds.top = 0; + offscreenBounds.left = 0; + offscreenBounds.right = CGBitmapContextGetWidth(currentContext); + offscreenBounds.bottom = CGBitmapContextGetHeight(currentContext); + GWorldPtr newOffscreenGWorld; + QDErr err = NewGWorldFromPtr(&newOffscreenGWorld, + getQDPixelFormatForBitmapContext(currentContext), &offscreenBounds, 0, 0, 0, + static_cast<char*>(offscreenData), CGBitmapContextGetBytesPerRow(currentContext)); + ASSERT(newOffscreenGWorld); + ASSERT(!err); + if (!err) { + if (offscreenGWorld) + DisposeGWorld(offscreenGWorld); + offscreenGWorld = newOffscreenGWorld; + + SetGWorld(offscreenGWorld, NULL); + + port = offscreenGWorld; + + nPort.qdPort.port = port; + boundsInWindow = [self bounds]; + + // Generate a QD origin based on the current affine transform for currentContext. + CGAffineTransform offscreenMatrix = CGContextGetCTM(currentContext); + CGPoint origin = {0,0}; + CGPoint axisFlip = {1,1}; + origin = CGPointApplyAffineTransform(origin, offscreenMatrix); + axisFlip = CGPointApplyAffineTransform(axisFlip, offscreenMatrix); + + // Quartz bitmaps have origins at the bottom left, but the axes may be inverted, so handle that. + origin.x = offscreenBounds.left - origin.x * (axisFlip.x - origin.x); + origin.y = offscreenBounds.bottom + origin.y * (axisFlip.y - origin.y); + + nPort.qdPort.portx = static_cast<int32_t>(-boundsInWindow.origin.x + origin.x); + nPort.qdPort.porty = static_cast<int32_t>(-boundsInWindow.origin.y - origin.y); + window.x = 0; + window.y = 0; + window.window = &nPort; + + // Use the clip bounds from the context instead of the bounds we created + // from the window above. + getNPRect(CGRectOffset(CGContextGetClipBoundingBox(currentContext), -origin.x, origin.y), window.clipRect); + } + } + } + + MacSetRectRgn(clipRegion, + window.clipRect.left + nPort.qdPort.portx, window.clipRect.top + nPort.qdPort.porty, + window.clipRect.right + nPort.qdPort.portx, window.clipRect.bottom + nPort.qdPort.porty); + + // Clip to the dirty region if drawing to a window. When drawing to another bitmap context, do not clip. + if ([NSGraphicsContext currentContext] == [[self currentWindow] graphicsContext]) { + // Clip to dirty region so plug-in does not draw over already-drawn regions of the window that are + // not going to be redrawn this update. This forces plug-ins to play nice with z-index ordering. + if (forUpdate) { + RgnHandle viewClipRegion = NewRgn(); + + // Get list of dirty rects from the opaque ancestor -- WebKit does some tricks with invalidation and + // display to enable z-ordering for NSViews; a side-effect of this is that only the WebHTMLView + // knows about the true set of dirty rects. + NSView *opaqueAncestor = [self opaqueAncestor]; + const NSRect *dirtyRects; + NSInteger dirtyRectCount, dirtyRectIndex; + [opaqueAncestor getRectsBeingDrawn:&dirtyRects count:&dirtyRectCount]; + + for (dirtyRectIndex = 0; dirtyRectIndex < dirtyRectCount; dirtyRectIndex++) { + NSRect dirtyRect = [self convertRect:dirtyRects[dirtyRectIndex] fromView:opaqueAncestor]; + if (!NSEqualSizes(dirtyRect.size, NSZeroSize)) { + // Create a region for this dirty rect + RgnHandle dirtyRectRegion = NewRgn(); + SetRectRgn(dirtyRectRegion, static_cast<short>(NSMinX(dirtyRect)), static_cast<short>(NSMinY(dirtyRect)), static_cast<short>(NSMaxX(dirtyRect)), static_cast<short>(NSMaxY(dirtyRect))); + + // Union this dirty rect with the rest of the dirty rects + UnionRgn(viewClipRegion, dirtyRectRegion, viewClipRegion); + DisposeRgn(dirtyRectRegion); + } + } + + // Intersect the dirty region with the clip region, so that we only draw over dirty parts + SectRgn(clipRegion, viewClipRegion, clipRegion); + DisposeRgn(viewClipRegion); + } + } + + // Switch to the port and set it up. + SetPort(port); + PenNormal(); + ForeColor(blackColor); + BackColor(whiteColor); + SetOrigin(nPort.qdPort.portx, nPort.qdPort.porty); + SetPortClipRegion(nPort.qdPort.port, clipRegion); + + if (forUpdate) { + // AppKit may have tried to help us by doing a BeginUpdate. + // But the invalid region at that level didn't include AppKit's notion of what was not valid. + // We reset the port's visible region to counteract what BeginUpdate did. + SetPortVisibleRegion(nPort.qdPort.port, clipRegion); + InvalWindowRgn(windowRef, clipRegion); + } + + qdPortState->forUpdate = forUpdate; + break; + } +#endif /* NP_NO_QUICKDRAW */ + + case NPDrawingModelCoreGraphics: { + if (![self canDraw]) { + portState = NULL; + break; + } + + ASSERT([NSView focusView] == self); + + CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]); + + PortState_CG *cgPortState = (PortState_CG *)malloc(sizeof(PortState_CG)); + portState = (PortState)cgPortState; + cgPortState->context = context; + +#ifndef NP_NO_CARBON + if (eventModel != NPEventModelCocoa) { + // Update the plugin's window/context + nPort.cgPort.window = windowRef; + nPort.cgPort.context = context; + window.window = &nPort.cgPort; + } +#endif /* NP_NO_CARBON */ + + // Save current graphics context's state; will be restored by -restorePortState: + CGContextSaveGState(context); + + // Clip to the dirty region if drawing to a window. When drawing to another bitmap context, do not clip. + if ([NSGraphicsContext currentContext] == [[self currentWindow] graphicsContext]) { + // Get list of dirty rects from the opaque ancestor -- WebKit does some tricks with invalidation and + // display to enable z-ordering for NSViews; a side-effect of this is that only the WebHTMLView + // knows about the true set of dirty rects. + NSView *opaqueAncestor = [self opaqueAncestor]; + const NSRect *dirtyRects; + NSInteger count; + [opaqueAncestor getRectsBeingDrawn:&dirtyRects count:&count]; + Vector<CGRect, 16> convertedDirtyRects; + convertedDirtyRects.resize(count); + for (int i = 0; i < count; ++i) + reinterpret_cast<NSRect&>(convertedDirtyRects[i]) = [self convertRect:dirtyRects[i] fromView:opaqueAncestor]; + CGContextClipToRects(context, convertedDirtyRects.data(), count); + } + + break; + } + + case NPDrawingModelCoreAnimation: + // Just set the port state to a dummy value. + portState = (PortState)1; + break; + + default: + ASSERT_NOT_REACHED(); + portState = NULL; + break; + } + + return portState; +} + +- (PortState)saveAndSetNewPortState +{ + return [self saveAndSetNewPortStateForUpdate:NO]; +} + +- (void)restorePortState:(PortState)portState +{ + ASSERT([self currentWindow]); + ASSERT(portState); + + switch (drawingModel) { +#ifndef NP_NO_QUICKDRAW + case NPDrawingModelQuickDraw: { + PortState_QD *qdPortState = (PortState_QD *)portState; + WindowRef windowRef = (WindowRef)[[self currentWindow] windowRef]; + CGrafPtr port = GetWindowPort(windowRef); + + SetPort(port); + + if (qdPortState->forUpdate) + ValidWindowRgn(windowRef, qdPortState->clipRegion); + + SetOrigin(qdPortState->oldOrigin.h, qdPortState->oldOrigin.v); + + SetPortClipRegion(port, qdPortState->oldClipRegion); + if (qdPortState->forUpdate) + SetPortVisibleRegion(port, qdPortState->oldVisibleRegion); + + DisposeRgn(qdPortState->oldClipRegion); + DisposeRgn(qdPortState->oldVisibleRegion); + DisposeRgn(qdPortState->clipRegion); + + SetGWorld(qdPortState->oldPort, qdPortState->oldDevice); + break; + } +#endif /* NP_NO_QUICKDRAW */ + + case NPDrawingModelCoreGraphics: { + ASSERT([NSView focusView] == self); + + CGContextRef context = ((PortState_CG *)portState)->context; + ASSERT(!nPort.cgPort.context || (context == nPort.cgPort.context)); + CGContextRestoreGState(context); + break; + } + + case NPDrawingModelCoreAnimation: + ASSERT(portState == (PortState)1); + break; + default: + ASSERT_NOT_REACHED(); + break; + } +} + +- (BOOL)sendEvent:(void*)event isDrawRect:(BOOL)eventIsDrawRect +{ + if (![self window]) + return NO; + ASSERT(event); + + if (!_isStarted) + return NO; + + ASSERT([_pluginPackage.get() pluginFuncs]->event); + + // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow. + // We probably don't want more general reentrancy protection; we are really + // protecting only against this one case, which actually comes up when + // you first install the SVG viewer plug-in. + if (inSetWindow) + return NO; + + Frame* frame = core([self webFrame]); + if (!frame) + return NO; + Page* page = frame->page(); + if (!page) + return NO; + + // Can only send drawRect (updateEvt) to CoreGraphics plugins when actually drawing + ASSERT((drawingModel != NPDrawingModelCoreGraphics) || !eventIsDrawRect || [NSView focusView] == self); + + PortState portState = NULL; + + if (isDrawingModelQuickDraw(drawingModel) || (drawingModel != NPDrawingModelCoreAnimation && eventIsDrawRect)) { + // In CoreGraphics mode, the port state only needs to be saved/set when redrawing the plug-in view. + // The plug-in is not allowed to draw at any other time. + portState = [self saveAndSetNewPortStateForUpdate:eventIsDrawRect]; + // We may have changed the window, so inform the plug-in. + [self setWindowIfNecessary]; + } + +#if !defined(NDEBUG) && !defined(NP_NO_QUICKDRAW) + // Draw green to help debug. + // If we see any green we know something's wrong. + // Note that PaintRect() only works for QuickDraw plugins; otherwise the current QD port is undefined. + if (isDrawingModelQuickDraw(drawingModel) && eventIsDrawRect) { + ForeColor(greenColor); + const ::Rect bigRect = { -10000, -10000, 10000, 10000 }; + PaintRect(&bigRect); + ForeColor(blackColor); + } +#endif + + // Temporarily retain self in case the plug-in view is released while sending an event. + [[self retain] autorelease]; + + BOOL acceptedEvent; + [self willCallPlugInFunction]; + // Set the pluginAllowPopup flag. + ASSERT(_eventHandler); + bool oldAllowPopups = frame->script()->allowPopupsFromPlugin(); + frame->script()->setAllowPopupsFromPlugin(_eventHandler->currentEventIsUserGesture()); + { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + acceptedEvent = [_pluginPackage.get() pluginFuncs]->event(plugin, event); + } + // Restore the old pluginAllowPopup flag. + frame->script()->setAllowPopupsFromPlugin(oldAllowPopups); + [self didCallPlugInFunction]; + + if (portState) { + if ([self currentWindow]) + [self restorePortState:portState]; + if (portState != (PortState)1) + free(portState); + } + + return acceptedEvent; +} + +- (void)windowFocusChanged:(BOOL)hasFocus +{ + _eventHandler->windowFocusChanged(hasFocus); +} + +- (void)sendDrawRectEvent:(NSRect)rect +{ + ASSERT(_eventHandler); + + CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext currentContext] graphicsPort]); + _eventHandler->drawRect(context, rect); +} + +- (void)stopTimers +{ + [super stopTimers]; + + if (_eventHandler) + _eventHandler->stopTimers(); + + if (!timers) + return; + + HashMap<uint32_t, PluginTimer*>::const_iterator end = timers->end(); + for (HashMap<uint32_t, PluginTimer*>::const_iterator it = timers->begin(); it != end; ++it) { + PluginTimer* timer = it->second; + timer->stop(); + } +} + +- (void)startTimers +{ + [super startTimers]; + + // If the plugin is completely obscured (scrolled out of view, for example), then we will + // send null events at a reduced rate. + _eventHandler->startTimers(_isCompletelyObscured); + + if (!timers) + return; + + HashMap<uint32_t, PluginTimer*>::const_iterator end = timers->end(); + for (HashMap<uint32_t, PluginTimer*>::const_iterator it = timers->begin(); it != end; ++it) { + PluginTimer* timer = it->second; + ASSERT(!timer->isActive()); + timer->start(_isCompletelyObscured); + } +} + +- (void)focusChanged +{ + // We need to null check the event handler here because + // the plug-in view can resign focus after it's been stopped + // and the event handler has been deleted. + if (_eventHandler) + _eventHandler->focusChanged(_hasFocus); +} + +- (void)mouseDown:(NSEvent *)theEvent +{ + if (!_isStarted) + return; + + _eventHandler->mouseDown(theEvent); +} + +- (void)mouseUp:(NSEvent *)theEvent +{ + if (!_isStarted) + return; + + _eventHandler->mouseUp(theEvent); +} + +- (void)handleMouseEntered:(NSEvent *)theEvent +{ + if (!_isStarted) + return; + + // Set cursor to arrow. Plugins often handle cursor internally, but those that don't will just get this default one. + [[NSCursor arrowCursor] set]; + + _eventHandler->mouseEntered(theEvent); +} + +- (void)handleMouseExited:(NSEvent *)theEvent +{ + if (!_isStarted) + return; + + _eventHandler->mouseExited(theEvent); + + // Set cursor back to arrow cursor. Because NSCursor doesn't know about changes that the plugin made, we could get confused about what we think the + // current cursor is otherwise. Therefore we have no choice but to unconditionally reset the cursor when the mouse exits the plugin. + [[NSCursor arrowCursor] set]; +} + +- (void)handleMouseMoved:(NSEvent *)theEvent +{ + if (!_isStarted) + return; + + _eventHandler->mouseMoved(theEvent); +} + +- (void)mouseDragged:(NSEvent *)theEvent +{ + if (!_isStarted) + return; + + _eventHandler->mouseDragged(theEvent); +} + +- (void)scrollWheel:(NSEvent *)theEvent +{ + if (!_isStarted) { + [super scrollWheel:theEvent]; + return; + } + + if (!_eventHandler->scrollWheel(theEvent)) + [super scrollWheel:theEvent]; +} + +- (void)keyUp:(NSEvent *)theEvent +{ + if (!_isStarted) + return; + + _eventHandler->keyUp(theEvent); +} + +- (void)keyDown:(NSEvent *)theEvent +{ + if (!_isStarted) + return; + + _eventHandler->keyDown(theEvent); +} + +- (void)flagsChanged:(NSEvent *)theEvent +{ + if (!_isStarted) + return; + + _eventHandler->flagsChanged(theEvent); +} + +- (void)sendModifierEventWithKeyCode:(int)keyCode character:(char)character +{ + if (!_isStarted) + return; + + _eventHandler->syntheticKeyDownWithCommandModifier(keyCode, character); +} + +- (void)privateBrowsingModeDidChange +{ + if (!_isStarted) + return; + + NPBool value = _isPrivateBrowsingEnabled; + + [self willCallPlugInFunction]; + { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + if ([_pluginPackage.get() pluginFuncs]->setvalue) + [_pluginPackage.get() pluginFuncs]->setvalue(plugin, NPNVprivateModeBool, &value); + } + [self didCallPlugInFunction]; +} + +// MARK: WEB_NETSCAPE_PLUGIN + +- (BOOL)isNewWindowEqualToOldWindow +{ + if (window.x != lastSetWindow.x) + return NO; + if (window.y != lastSetWindow.y) + return NO; + if (window.width != lastSetWindow.width) + return NO; + if (window.height != lastSetWindow.height) + return NO; + if (window.clipRect.top != lastSetWindow.clipRect.top) + return NO; + if (window.clipRect.left != lastSetWindow.clipRect.left) + return NO; + if (window.clipRect.bottom != lastSetWindow.clipRect.bottom) + return NO; + if (window.clipRect.right != lastSetWindow.clipRect.right) + return NO; + if (window.type != lastSetWindow.type) + return NO; + + switch (drawingModel) { +#ifndef NP_NO_QUICKDRAW + case NPDrawingModelQuickDraw: + if (nPort.qdPort.portx != lastSetPort.qdPort.portx) + return NO; + if (nPort.qdPort.porty != lastSetPort.qdPort.porty) + return NO; + if (nPort.qdPort.port != lastSetPort.qdPort.port) + return NO; + break; +#endif /* NP_NO_QUICKDRAW */ + + case NPDrawingModelCoreGraphics: + if (nPort.cgPort.window != lastSetPort.cgPort.window) + return NO; + if (nPort.cgPort.context != lastSetPort.cgPort.context) + return NO; + break; + + case NPDrawingModelCoreAnimation: + if (window.window != lastSetWindow.window) + return NO; + break; + default: + ASSERT_NOT_REACHED(); + break; + } + + return YES; +} + +-(void)tellQuickTimeToChill +{ +#ifndef NP_NO_QUICKDRAW + ASSERT(isDrawingModelQuickDraw(drawingModel)); + + // Make a call to the secret QuickDraw API that makes QuickTime calm down. + WindowRef windowRef = (WindowRef)[[self window] windowRef]; + if (!windowRef) { + return; + } + CGrafPtr port = GetWindowPort(windowRef); + ::Rect bounds; + GetPortBounds(port, &bounds); + WKCallDrawingNotification(port, &bounds); +#endif /* NP_NO_QUICKDRAW */ +} + +- (void)updateAndSetWindow +{ + // A plug-in can only update if it's (1) already been started (2) isn't stopped + // and (3) is able to draw on-screen. To meet condition (3) the plug-in must not + // be hidden and be attached to a window. There are two exceptions to this rule: + // + // Exception 1: QuickDraw plug-ins must be manually told when to stop writing + // bits to the window backing store, thus to do so requires a new call to + // NPP_SetWindow() with an empty NPWindow struct. + // + // Exception 2: CoreGraphics plug-ins expect to have their drawable area updated + // when they are moved to a background tab, via a NPP_SetWindow call. This is + // accomplished by allowing -saveAndSetNewPortStateForUpdate to "clip-out" the window's + // clipRect. Flash is curently an exception to this. See 6453738. + // + + if (!_isStarted) + return; + +#ifdef NP_NO_QUICKDRAW + if (![self canDraw]) + return; +#else + if (drawingModel == NPDrawingModelQuickDraw) + [self tellQuickTimeToChill]; + else if (drawingModel == NPDrawingModelCoreGraphics && ![self canDraw] && _isFlash) { + // The Flash plug-in does not expect an NPP_SetWindow call from WebKit in this case. + // See Exception 2 above. + return; + } +#endif // NP_NO_QUICKDRAW + + BOOL didLockFocus = [NSView focusView] != self && [self lockFocusIfCanDraw]; + + PortState portState = [self saveAndSetNewPortState]; + if (portState) { + [self setWindowIfNecessary]; + [self restorePortState:portState]; + if (portState != (PortState)1) + free(portState); + } else if (drawingModel == NPDrawingModelCoreGraphics) + [self setWindowIfNecessary]; + + if (didLockFocus) + [self unlockFocus]; +} + +- (void)setWindowIfNecessary +{ + if (!_isStarted) + return; + + if (![self isNewWindowEqualToOldWindow]) { + // Make sure we don't call NPP_HandleEvent while we're inside NPP_SetWindow. + // We probably don't want more general reentrancy protection; we are really + // protecting only against this one case, which actually comes up when + // you first install the SVG viewer plug-in. + NPError npErr; + + BOOL wasInSetWindow = inSetWindow; + inSetWindow = YES; + [self willCallPlugInFunction]; + { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + npErr = [_pluginPackage.get() pluginFuncs]->setwindow(plugin, &window); + } + [self didCallPlugInFunction]; + inSetWindow = wasInSetWindow; + +#ifndef NDEBUG + switch (drawingModel) { +#ifndef NP_NO_QUICKDRAW + case NPDrawingModelQuickDraw: + LOG(Plugins, "NPP_SetWindow (QuickDraw): %d, port=0x%08x, window.x:%d window.y:%d window.width:%d window.height:%d", + npErr, (int)nPort.qdPort.port, (int)window.x, (int)window.y, (int)window.width, (int)window.height); + break; +#endif /* NP_NO_QUICKDRAW */ + + case NPDrawingModelCoreGraphics: + LOG(Plugins, "NPP_SetWindow (CoreGraphics): %d, window=%p, context=%p, window.x:%d window.y:%d window.width:%d window.height:%d window.clipRect size:%dx%d", + npErr, nPort.cgPort.window, nPort.cgPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height, + window.clipRect.right - window.clipRect.left, window.clipRect.bottom - window.clipRect.top); + break; + + case NPDrawingModelCoreAnimation: + LOG(Plugins, "NPP_SetWindow (CoreAnimation): %d, window=%p window.x:%d window.y:%d window.width:%d window.height:%d", + npErr, window.window, nPort.cgPort.context, (int)window.x, (int)window.y, (int)window.width, (int)window.height); + break; + + default: + ASSERT_NOT_REACHED(); + break; + } +#endif /* !defined(NDEBUG) */ + + lastSetWindow = window; + lastSetPort = nPort; + } +} + ++ (void)setCurrentPluginView:(WebNetscapePluginView *)view +{ + currentPluginView = view; +} + ++ (WebNetscapePluginView *)currentPluginView +{ + return currentPluginView; +} + +- (BOOL)createPlugin +{ + // Open the plug-in package so it remains loaded while our plugin uses it + [_pluginPackage.get() open]; + + // Initialize drawingModel to an invalid value so that we can detect when the plugin does not specify a drawingModel + drawingModel = (NPDrawingModel)-1; + + // Initialize eventModel to an invalid value so that we can detect when the plugin does not specify an event model. + eventModel = (NPEventModel)-1; + + NPError npErr = [self _createPlugin]; + if (npErr != NPERR_NO_ERROR) { + LOG_ERROR("NPP_New failed with error: %d", npErr); + [self _destroyPlugin]; + [_pluginPackage.get() close]; + return NO; + } + + if (drawingModel == (NPDrawingModel)-1) { +#ifndef NP_NO_QUICKDRAW + // Default to QuickDraw if the plugin did not specify a drawing model. + drawingModel = NPDrawingModelQuickDraw; +#else + // QuickDraw is not available, so we can't default to it. Instead, default to CoreGraphics. + drawingModel = NPDrawingModelCoreGraphics; +#endif + } + + if (eventModel == (NPEventModel)-1) { + // If the plug-in did not specify a drawing model we default to Carbon when it is available. +#ifndef NP_NO_CARBON + eventModel = NPEventModelCarbon; +#else + eventModel = NPEventModelCocoa; +#endif // NP_NO_CARBON + } + +#ifndef NP_NO_CARBON + if (eventModel == NPEventModelCocoa && isDrawingModelQuickDraw(drawingModel)) { + LOG(Plugins, "Plugin can't use use Cocoa event model with QuickDraw drawing model: %@", _pluginPackage.get()); + [self _destroyPlugin]; + [_pluginPackage.get() close]; + + return NO; + } +#endif // NP_NO_CARBON + +#ifndef BUILDING_ON_TIGER + if (drawingModel == NPDrawingModelCoreAnimation) { + void *value = 0; + if ([_pluginPackage.get() pluginFuncs]->getvalue(plugin, NPPVpluginCoreAnimationLayer, &value) == NPERR_NO_ERROR && value) { + + // The plug-in gives us a retained layer. + _pluginLayer.adoptNS((CALayer *)value); + + BOOL accleratedCompositingEnabled = false; +#if USE(ACCELERATED_COMPOSITING) + accleratedCompositingEnabled = [[[self webView] preferences] acceleratedCompositingEnabled]; +#endif + if (accleratedCompositingEnabled) { + // FIXME: This code can be shared between WebHostedNetscapePluginView and WebNetscapePluginView. +#ifndef BUILDING_ON_LEOPARD + // Since this layer isn't going to be inserted into a view, we need to create another layer and flip its geometry + // in order to get the coordinate system right. + RetainPtr<CALayer> realPluginLayer(AdoptNS, _pluginLayer.releaseRef()); + + _pluginLayer.adoptNS([[CALayer alloc] init]); + _pluginLayer.get().bounds = realPluginLayer.get().bounds; + _pluginLayer.get().geometryFlipped = YES; + + realPluginLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable; + [_pluginLayer.get() addSublayer:realPluginLayer.get()]; +#endif + // Eagerly enter compositing mode, since we know we'll need it. This avoids firing setNeedsStyleRecalc() + // for iframes that contain composited plugins at bad times. https://bugs.webkit.org/show_bug.cgi?id=39033 + core([self webFrame])->view()->enterCompositingMode(); + [self element]->setNeedsStyleRecalc(SyntheticStyleChange); + } else + [self setWantsLayer:YES]; + + LOG(Plugins, "%@ is using Core Animation drawing model with layer %@", _pluginPackage.get(), _pluginLayer.get()); + } + + ASSERT(_pluginLayer); + } +#endif + + // Create the event handler + _eventHandler.set(WebNetscapePluginEventHandler::create(self)); + + return YES; +} + +#ifndef BUILDING_ON_TIGER +// FIXME: This method is an ideal candidate to move up to the base class +- (CALayer *)pluginLayer +{ + return _pluginLayer.get(); +} + +- (void)setLayer:(CALayer *)newLayer +{ + [super setLayer:newLayer]; + + if (newLayer && _pluginLayer) { + _pluginLayer.get().frame = [newLayer frame]; + _pluginLayer.get().autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable; + [newLayer addSublayer:_pluginLayer.get()]; + } +} +#endif + +- (void)loadStream +{ + if ([self _shouldCancelSrcStream]) + return; + + if (_loadManually) { + [self _redeliverStream]; + return; + } + + // If the OBJECT/EMBED tag has no SRC, the URL is passed to us as "". + // Check for this and don't start a load in this case. + if (_sourceURL && ![_sourceURL.get() _web_isEmpty]) { + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:_sourceURL.get()]; + [request _web_setHTTPReferrer:core([self webFrame])->loader()->outgoingReferrer()]; + [self loadRequest:request inTarget:nil withNotifyData:nil sendNotification:NO]; + } +} + +- (BOOL)shouldStop +{ + // If we're already calling a plug-in function, do not call NPP_Destroy(). The plug-in function we are calling + // may assume that its instance->pdata, or other memory freed by NPP_Destroy(), is valid and unchanged until said + // plugin-function returns. + // See <rdar://problem/4480737>. + if (pluginFunctionCallDepth > 0) { + shouldStopSoon = YES; + return NO; + } + + return YES; +} + +- (void)destroyPlugin +{ + // To stop active streams it's necessary to invoke stop() on a copy + // of streams. This is because calling WebNetscapePluginStream::stop() also has the side effect + // of removing a stream from this hash set. + Vector<RefPtr<WebNetscapePluginStream> > streamsCopy; + copyToVector(streams, streamsCopy); + for (size_t i = 0; i < streamsCopy.size(); i++) + streamsCopy[i]->stop(); + + [[_pendingFrameLoads.get() allKeys] makeObjectsPerformSelector:@selector(_setInternalLoadDelegate:) withObject:nil]; + [NSObject cancelPreviousPerformRequestsWithTarget:self]; + + // Setting the window type to 0 ensures that NPP_SetWindow will be called if the plug-in is restarted. + lastSetWindow.type = (NPWindowType)0; + +#ifndef BUILDING_ON_TIGER + _pluginLayer = 0; +#endif + + [self _destroyPlugin]; + [_pluginPackage.get() close]; + + _eventHandler.clear(); +} + +- (NPEventModel)eventModel +{ + return eventModel; +} + +- (NPP)plugin +{ + return plugin; +} + +- (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values +{ + ASSERT([keys count] == [values count]); + + // Convert the attributes to 2 C string arrays. + // These arrays are passed to NPP_New, but the strings need to be + // modifiable and live the entire life of the plugin. + + // The Java plug-in requires the first argument to be the base URL + if ([_MIMEType.get() isEqualToString:@"application/x-java-applet"]) { + cAttributes = (char **)malloc(([keys count] + 1) * sizeof(char *)); + cValues = (char **)malloc(([values count] + 1) * sizeof(char *)); + cAttributes[0] = strdup("DOCBASE"); + cValues[0] = strdup([_baseURL.get() _web_URLCString]); + argsCount++; + } else { + cAttributes = (char **)malloc([keys count] * sizeof(char *)); + cValues = (char **)malloc([values count] * sizeof(char *)); + } + + BOOL isWMP = [_pluginPackage.get() bundleIdentifier] == "com.microsoft.WMP.defaultplugin"; + + unsigned i; + unsigned count = [keys count]; + for (i = 0; i < count; i++) { + NSString *key = [keys objectAtIndex:i]; + NSString *value = [values objectAtIndex:i]; + if ([key _webkit_isCaseInsensitiveEqualToString:@"height"]) { + specifiedHeight = [value intValue]; + } else if ([key _webkit_isCaseInsensitiveEqualToString:@"width"]) { + specifiedWidth = [value intValue]; + } + // Avoid Window Media Player crash when these attributes are present. + if (isWMP && ([key _webkit_isCaseInsensitiveEqualToString:@"SAMIStyle"] || [key _webkit_isCaseInsensitiveEqualToString:@"SAMILang"])) { + continue; + } + cAttributes[argsCount] = strdup([key UTF8String]); + cValues[argsCount] = strdup([value UTF8String]); + LOG(Plugins, "%@ = %@", key, value); + argsCount++; + } +} + +- (uint32_t)checkIfAllowedToLoadURL:(const char*)urlCString frame:(const char*)frameNameCString + callbackFunc:(void (*)(NPP npp, uint32_t checkID, NPBool allowed, void* context))callbackFunc + context:(void*)context +{ + if (!_containerChecksInProgress) + _containerChecksInProgress = [[NSMutableDictionary alloc] init]; + + NSString *frameName = frameNameCString ? [NSString stringWithCString:frameNameCString encoding:NSISOLatin1StringEncoding] : nil; + + ++_currentContainerCheckRequestID; + WebNetscapeContainerCheckContextInfo *contextInfo = [[WebNetscapeContainerCheckContextInfo alloc] initWithCheckRequestID:_currentContainerCheckRequestID + callbackFunc:callbackFunc + context:context]; + + WebPluginContainerCheck *check = [WebPluginContainerCheck checkWithRequest:[self requestWithURLCString:urlCString] + target:frameName + resultObject:self + selector:@selector(_containerCheckResult:contextInfo:) + controller:self + contextInfo:contextInfo]; + + [contextInfo release]; + [_containerChecksInProgress setObject:check forKey:[NSNumber numberWithInt:_currentContainerCheckRequestID]]; + [check start]; + + return _currentContainerCheckRequestID; +} + +- (void)_containerCheckResult:(PolicyAction)policy contextInfo:(id)contextInfo +{ + ASSERT([contextInfo isKindOfClass:[WebNetscapeContainerCheckContextInfo class]]); + void (*pluginCallback)(NPP npp, uint32_t, NPBool, void*) = [contextInfo callback]; + + if (!pluginCallback) { + ASSERT_NOT_REACHED(); + return; + } + + pluginCallback([self plugin], [contextInfo checkRequestID], (policy == PolicyUse), [contextInfo context]); +} + +- (void)cancelCheckIfAllowedToLoadURL:(uint32_t)checkID +{ + WebPluginContainerCheck *check = (WebPluginContainerCheck *)[_containerChecksInProgress objectForKey:[NSNumber numberWithInt:checkID]]; + + if (!check) + return; + + [check cancel]; + [_containerChecksInProgress removeObjectForKey:[NSNumber numberWithInt:checkID]]; +} + +// WebPluginContainerCheck automatically calls this method after invoking our _containerCheckResult: selector. +// It works this way because calling -[WebPluginContainerCheck cancel] allows it to do it's teardown process. +- (void)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)webPluginContainerCheck +{ + ASSERT([webPluginContainerCheck isKindOfClass:[WebPluginContainerCheck class]]); + WebPluginContainerCheck *check = (WebPluginContainerCheck *)webPluginContainerCheck; + ASSERT([[check contextInfo] isKindOfClass:[WebNetscapeContainerCheckContextInfo class]]); + + [self cancelCheckIfAllowedToLoadURL:[[check contextInfo] checkRequestID]]; +} + +#ifdef BUILDING_ON_TIGER +// The Tiger compiler requires these two methods be present. Otherwise it doesn't think WebNetscapePluginView +// conforms to the WebPluginContainerCheckController protocol. +- (WebView *)webView +{ + return [super webView]; +} + +- (WebFrame *)webFrame +{ + return [super webFrame]; +} +#endif + +// MARK: NSVIEW + +- (id)initWithFrame:(NSRect)frame + pluginPackage:(WebNetscapePluginPackage *)pluginPackage + URL:(NSURL *)URL + baseURL:(NSURL *)baseURL + MIMEType:(NSString *)MIME + attributeKeys:(NSArray *)keys + attributeValues:(NSArray *)values + loadManually:(BOOL)loadManually + element:(PassRefPtr<WebCore::HTMLPlugInElement>)element +{ + self = [super initWithFrame:frame pluginPackage:pluginPackage URL:URL baseURL:baseURL MIMEType:MIME attributeKeys:keys attributeValues:values loadManually:loadManually element:element]; + if (!self) + return nil; + + _pendingFrameLoads.adoptNS([[NSMutableDictionary alloc] init]); + + // load the plug-in if it is not already loaded + if (![pluginPackage load]) { + [self release]; + return nil; + } + + return self; +} + +- (id)initWithFrame:(NSRect)frame +{ + ASSERT_NOT_REACHED(); + return nil; +} + +- (void)fini +{ +#ifndef NP_NO_QUICKDRAW + if (offscreenGWorld) + DisposeGWorld(offscreenGWorld); +#endif + + for (unsigned i = 0; i < argsCount; i++) { + free(cAttributes[i]); + free(cValues[i]); + } + free(cAttributes); + free(cValues); + + ASSERT(!_eventHandler); + + if (timers) { + deleteAllValues(*timers); + delete timers; + } + + [_containerChecksInProgress release]; +} + +- (void)disconnectStream:(WebNetscapePluginStream*)stream +{ + streams.remove(stream); +} + +- (void)dealloc +{ + ASSERT(!_isStarted); + ASSERT(!plugin); + + [self fini]; + + [super dealloc]; +} + +- (void)finalize +{ + ASSERT_MAIN_THREAD(); + ASSERT(!_isStarted); + + [self fini]; + + [super finalize]; +} + +- (void)drawRect:(NSRect)rect +{ + if (_cachedSnapshot) { + NSRect sourceRect = { NSZeroPoint, [_cachedSnapshot.get() size] }; + [_cachedSnapshot.get() drawInRect:[self bounds] fromRect:sourceRect operation:NSCompositeSourceOver fraction:1]; + return; + } + + if (drawingModel == NPDrawingModelCoreAnimation && (!_snapshotting || ![self supportsSnapshotting])) + return; + + if (!_isStarted) + return; + + if ([NSGraphicsContext currentContextDrawingToScreen] || _isFlash) + [self sendDrawRectEvent:rect]; + else { + NSBitmapImageRep *printedPluginBitmap = [self _printedPluginBitmap]; + if (printedPluginBitmap) { + // Flip the bitmap before drawing because the QuickDraw port is flipped relative + // to this view. + CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; + CGContextSaveGState(cgContext); + NSRect bounds = [self bounds]; + CGContextTranslateCTM(cgContext, 0.0f, NSHeight(bounds)); + CGContextScaleCTM(cgContext, 1.0f, -1.0f); + [printedPluginBitmap drawInRect:bounds]; + CGContextRestoreGState(cgContext); + } + } +} + +- (NPObject *)createPluginScriptableObject +{ + if (![_pluginPackage.get() pluginFuncs]->getvalue || !_isStarted) + return NULL; + + NPObject *value = NULL; + NPError error; + [self willCallPlugInFunction]; + { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + error = [_pluginPackage.get() pluginFuncs]->getvalue(plugin, NPPVpluginScriptableNPObject, &value); + } + [self didCallPlugInFunction]; + if (error != NPERR_NO_ERROR) + return NULL; + + return value; +} + +- (void)willCallPlugInFunction +{ + ASSERT(plugin); + + // Could try to prevent infinite recursion here, but it's probably not worth the effort. + pluginFunctionCallDepth++; +} + +- (void)didCallPlugInFunction +{ + ASSERT(pluginFunctionCallDepth > 0); + pluginFunctionCallDepth--; + + // If -stop was called while we were calling into a plug-in function, and we're no longer + // inside a plug-in function, stop now. + if (pluginFunctionCallDepth == 0 && shouldStopSoon) { + shouldStopSoon = NO; + [self stop]; + } +} + +-(void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response +{ + ASSERT(_loadManually); + ASSERT(!_manualStream); + + _manualStream = WebNetscapePluginStream::create(core([self webFrame])->loader()); +} + +- (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data +{ + ASSERT(_loadManually); + ASSERT(_manualStream); + + _dataLengthReceived += [data length]; + + if (!_isStarted) + return; + + if (!_manualStream->plugin()) { + // Check if the load should be cancelled + if ([self _shouldCancelSrcStream]) { + NSURLResponse *response = [[self dataSource] response]; + + NSError *error = [[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInWillHandleLoad + contentURL:[response URL] + pluginPageURL:nil + pluginName:nil // FIXME: Get this from somewhere + MIMEType:[response MIMEType]]; + [[self dataSource] _documentLoader]->cancelMainResourceLoad(error); + [error release]; + return; + } + + _manualStream->setRequestURL([[[self dataSource] request] URL]); + _manualStream->setPlugin([self plugin]); + ASSERT(_manualStream->plugin()); + + _manualStream->startStreamWithResponse([[self dataSource] response]); + } + + if (_manualStream->plugin()) + _manualStream->didReceiveData(0, static_cast<const char *>([data bytes]), [data length]); +} + +- (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error +{ + ASSERT(_loadManually); + + _error = error; + + if (!_isStarted) { + return; + } + + _manualStream->destroyStreamWithError(error); +} + +- (void)pluginViewFinishedLoading:(NSView *)pluginView +{ + ASSERT(_loadManually); + ASSERT(_manualStream); + + if (_isStarted) + _manualStream->didFinishLoading(0); +} + +- (NSTextInputContext *)inputContext +{ + return nil; +} + +@end + +@implementation WebNetscapePluginView (WebNPPCallbacks) + +- (void)evaluateJavaScriptPluginRequest:(WebPluginRequest *)JSPluginRequest +{ + // FIXME: Is this isStarted check needed here? evaluateJavaScriptPluginRequest should not be called + // if we are stopped since this method is called after a delay and we call + // cancelPreviousPerformRequestsWithTarget inside of stop. + if (!_isStarted) { + return; + } + + NSURL *URL = [[JSPluginRequest request] URL]; + NSString *JSString = [URL _webkit_scriptIfJavaScriptURL]; + ASSERT(JSString); + + NSString *result = [[self webFrame] _stringByEvaluatingJavaScriptFromString:JSString forceUserGesture:[JSPluginRequest isCurrentEventUserGesture]]; + + // Don't continue if stringByEvaluatingJavaScriptFromString caused the plug-in to stop. + if (!_isStarted) { + return; + } + + if ([JSPluginRequest frameName] != nil) { + // FIXME: If the result is a string, we probably want to put that string into the frame. + if ([JSPluginRequest sendNotification]) { + [self willCallPlugInFunction]; + { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + [_pluginPackage.get() pluginFuncs]->urlnotify(plugin, [URL _web_URLCString], NPRES_DONE, [JSPluginRequest notifyData]); + } + [self didCallPlugInFunction]; + } + } else if ([result length] > 0) { + // Don't call NPP_NewStream and other stream methods if there is no JS result to deliver. This is what Mozilla does. + NSData *JSData = [result dataUsingEncoding:NSUTF8StringEncoding]; + + RefPtr<WebNetscapePluginStream> stream = WebNetscapePluginStream::create([NSURLRequest requestWithURL:URL], plugin, [JSPluginRequest sendNotification], [JSPluginRequest notifyData]); + + RetainPtr<NSURLResponse> response(AdoptNS, [[NSURLResponse alloc] initWithURL:URL + MIMEType:@"text/plain" + expectedContentLength:[JSData length] + textEncodingName:nil]); + + stream->startStreamWithResponse(response.get()); + stream->didReceiveData(0, static_cast<const char*>([JSData bytes]), [JSData length]); + stream->didFinishLoading(0); + } +} + +- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithReason:(NPReason)reason +{ + ASSERT(_isStarted); + + WebPluginRequest *pluginRequest = [_pendingFrameLoads.get() objectForKey:webFrame]; + ASSERT(pluginRequest != nil); + ASSERT([pluginRequest sendNotification]); + + [self willCallPlugInFunction]; + { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + [_pluginPackage.get() pluginFuncs]->urlnotify(plugin, [[[pluginRequest request] URL] _web_URLCString], reason, [pluginRequest notifyData]); + } + [self didCallPlugInFunction]; + + [_pendingFrameLoads.get() removeObjectForKey:webFrame]; + [webFrame _setInternalLoadDelegate:nil]; +} + +- (void)webFrame:(WebFrame *)webFrame didFinishLoadWithError:(NSError *)error +{ + NPReason reason = NPRES_DONE; + if (error != nil) + reason = WebNetscapePluginStream::reasonForError(error); + [self webFrame:webFrame didFinishLoadWithReason:reason]; +} + +- (void)loadPluginRequest:(WebPluginRequest *)pluginRequest +{ + NSURLRequest *request = [pluginRequest request]; + NSString *frameName = [pluginRequest frameName]; + WebFrame *frame = nil; + + NSURL *URL = [request URL]; + NSString *JSString = [URL _webkit_scriptIfJavaScriptURL]; + + ASSERT(frameName || JSString); + + if (frameName) { + // FIXME - need to get rid of this window creation which + // bypasses normal targeted link handling + frame = kit(core([self webFrame])->loader()->findFrameForNavigation(frameName)); + if (frame == nil) { + WebView *currentWebView = [self webView]; + NSDictionary *features = [[NSDictionary alloc] init]; + WebView *newWebView = [[currentWebView _UIDelegateForwarder] webView:currentWebView + createWebViewWithRequest:nil + windowFeatures:features]; + [features release]; + + if (!newWebView) { + if ([pluginRequest sendNotification]) { + [self willCallPlugInFunction]; + { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + [_pluginPackage.get() pluginFuncs]->urlnotify(plugin, [[[pluginRequest request] URL] _web_URLCString], NPERR_GENERIC_ERROR, [pluginRequest notifyData]); + } + [self didCallPlugInFunction]; + } + return; + } + + frame = [newWebView mainFrame]; + core(frame)->tree()->setName(frameName); + [[newWebView _UIDelegateForwarder] webViewShow:newWebView]; + } + } + + if (JSString) { + ASSERT(frame == nil || [self webFrame] == frame); + [self evaluateJavaScriptPluginRequest:pluginRequest]; + } else { + [frame loadRequest:request]; + if ([pluginRequest sendNotification]) { + // Check if another plug-in view or even this view is waiting for the frame to load. + // If it is, tell it that the load was cancelled because it will be anyway. + WebNetscapePluginView *view = [frame _internalLoadDelegate]; + if (view != nil) { + ASSERT([view isKindOfClass:[WebNetscapePluginView class]]); + [view webFrame:frame didFinishLoadWithReason:NPRES_USER_BREAK]; + } + [_pendingFrameLoads.get() _webkit_setObject:pluginRequest forUncopiedKey:frame]; + [frame _setInternalLoadDelegate:self]; + } + } +} + +- (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification +{ + NSURL *URL = [request URL]; + + if (!URL) + return NPERR_INVALID_URL; + + // Don't allow requests to be loaded when the document loader is stopping all loaders. + if ([[self dataSource] _documentLoader]->isStopping()) + return NPERR_GENERIC_ERROR; + + NSString *target = nil; + if (cTarget) { + // Find the frame given the target string. + target = [NSString stringWithCString:cTarget encoding:NSISOLatin1StringEncoding]; + } + WebFrame *frame = [self webFrame]; + + // don't let a plugin start any loads if it is no longer part of a document that is being + // displayed unless the loads are in the same frame as the plugin. + if ([[self dataSource] _documentLoader] != core([self webFrame])->loader()->activeDocumentLoader() && + (!cTarget || [frame findFrameNamed:target] != frame)) { + return NPERR_GENERIC_ERROR; + } + + NSString *JSString = [URL _webkit_scriptIfJavaScriptURL]; + if (JSString != nil) { + if (![[[self webView] preferences] isJavaScriptEnabled]) { + // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does. + return NPERR_GENERIC_ERROR; + } else if (cTarget == NULL && _mode == NP_FULL) { + // Don't allow a JavaScript request from a standalone plug-in that is self-targetted + // because this can cause the user to be redirected to a blank page (3424039). + return NPERR_INVALID_PARAM; + } + } else { + if (!core([self webFrame])->document()->securityOrigin()->canDisplay(URL)) + return NPERR_GENERIC_ERROR; + } + + if (cTarget || JSString) { + // Make when targetting a frame or evaluating a JS string, perform the request after a delay because we don't + // want to potentially kill the plug-in inside of its URL request. + + if (JSString && target && [frame findFrameNamed:target] != frame) { + // For security reasons, only allow JS requests to be made on the frame that contains the plug-in. + return NPERR_INVALID_PARAM; + } + + bool currentEventIsUserGesture = false; + if (_eventHandler) + currentEventIsUserGesture = _eventHandler->currentEventIsUserGesture(); + + WebPluginRequest *pluginRequest = [[WebPluginRequest alloc] initWithRequest:request + frameName:target + notifyData:notifyData + sendNotification:sendNotification + didStartFromUserGesture:currentEventIsUserGesture]; + [self performSelector:@selector(loadPluginRequest:) withObject:pluginRequest afterDelay:0]; + [pluginRequest release]; + } else { + RefPtr<WebNetscapePluginStream> stream = WebNetscapePluginStream::create(request, plugin, sendNotification, notifyData); + + streams.add(stream.get()); + stream->start(); + } + + return NPERR_NO_ERROR; +} + +-(NPError)getURLNotify:(const char *)URLCString target:(const char *)cTarget notifyData:(void *)notifyData +{ + LOG(Plugins, "NPN_GetURLNotify: %s target: %s", URLCString, cTarget); + + NSMutableURLRequest *request = [self requestWithURLCString:URLCString]; + return [self loadRequest:request inTarget:cTarget withNotifyData:notifyData sendNotification:YES]; +} + +-(NPError)getURL:(const char *)URLCString target:(const char *)cTarget +{ + LOG(Plugins, "NPN_GetURL: %s target: %s", URLCString, cTarget); + + NSMutableURLRequest *request = [self requestWithURLCString:URLCString]; + return [self loadRequest:request inTarget:cTarget withNotifyData:NULL sendNotification:NO]; +} + +- (NPError)_postURL:(const char *)URLCString + target:(const char *)target + len:(UInt32)len + buf:(const char *)buf + file:(NPBool)file + notifyData:(void *)notifyData + sendNotification:(BOOL)sendNotification + allowHeaders:(BOOL)allowHeaders +{ + if (!URLCString || !len || !buf) { + return NPERR_INVALID_PARAM; + } + + NSData *postData = nil; + + if (file) { + // If we're posting a file, buf is either a file URL or a path to the file. + NSString *bufString = (NSString *)CFStringCreateWithCString(kCFAllocatorDefault, buf, kCFStringEncodingWindowsLatin1); + if (!bufString) { + return NPERR_INVALID_PARAM; + } + NSURL *fileURL = [NSURL _web_URLWithDataAsString:bufString]; + NSString *path; + if ([fileURL isFileURL]) { + path = [fileURL path]; + } else { + path = bufString; + } + postData = [NSData dataWithContentsOfFile:[path _webkit_fixedCarbonPOSIXPath]]; + CFRelease(bufString); + if (!postData) { + return NPERR_FILE_NOT_FOUND; + } + } else { + postData = [NSData dataWithBytes:buf length:len]; + } + + if ([postData length] == 0) { + return NPERR_INVALID_PARAM; + } + + NSMutableURLRequest *request = [self requestWithURLCString:URLCString]; + [request setHTTPMethod:@"POST"]; + + if (allowHeaders) { + if ([postData _web_startsWithBlankLine]) { + postData = [postData subdataWithRange:NSMakeRange(1, [postData length] - 1)]; + } else { + NSInteger location = [postData _web_locationAfterFirstBlankLine]; + if (location != NSNotFound) { + // If the blank line is somewhere in the middle of postData, everything before is the header. + NSData *headerData = [postData subdataWithRange:NSMakeRange(0, location)]; + NSMutableDictionary *header = [headerData _webkit_parseRFC822HeaderFields]; + unsigned dataLength = [postData length] - location; + + // Sometimes plugins like to set Content-Length themselves when they post, + // but WebFoundation does not like that. So we will remove the header + // and instead truncate the data to the requested length. + NSString *contentLength = [header objectForKey:@"Content-Length"]; + + if (contentLength != nil) + dataLength = min<unsigned>([contentLength intValue], dataLength); + [header removeObjectForKey:@"Content-Length"]; + + if ([header count] > 0) { + [request setAllHTTPHeaderFields:header]; + } + // Everything after the blank line is the actual content of the POST. + postData = [postData subdataWithRange:NSMakeRange(location, dataLength)]; + + } + } + if ([postData length] == 0) { + return NPERR_INVALID_PARAM; + } + } + + // Plug-ins expect to receive uncached data when doing a POST (3347134). + [request setCachePolicy:NSURLRequestReloadIgnoringCacheData]; + [request setHTTPBody:postData]; + + return [self loadRequest:request inTarget:target withNotifyData:notifyData sendNotification:sendNotification]; +} + +- (NPError)postURLNotify:(const char *)URLCString + target:(const char *)target + len:(UInt32)len + buf:(const char *)buf + file:(NPBool)file + notifyData:(void *)notifyData +{ + LOG(Plugins, "NPN_PostURLNotify: %s", URLCString); + return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:notifyData sendNotification:YES allowHeaders:YES]; +} + +-(NPError)postURL:(const char *)URLCString + target:(const char *)target + len:(UInt32)len + buf:(const char *)buf + file:(NPBool)file +{ + LOG(Plugins, "NPN_PostURL: %s", URLCString); + // As documented, only allow headers to be specified via NPP_PostURL when using a file. + return [self _postURL:URLCString target:target len:len buf:buf file:file notifyData:NULL sendNotification:NO allowHeaders:file]; +} + +-(NPError)newStream:(NPMIMEType)type target:(const char *)target stream:(NPStream**)stream +{ + LOG(Plugins, "NPN_NewStream"); + return NPERR_GENERIC_ERROR; +} + +-(NPError)write:(NPStream*)stream len:(SInt32)len buffer:(void *)buffer +{ + LOG(Plugins, "NPN_Write"); + return NPERR_GENERIC_ERROR; +} + +-(NPError)destroyStream:(NPStream*)stream reason:(NPReason)reason +{ + LOG(Plugins, "NPN_DestroyStream"); + // This function does a sanity check to ensure that the NPStream provided actually + // belongs to the plug-in that provided it, which fixes a crash in the DivX + // plug-in: <rdar://problem/5093862> | http://bugs.webkit.org/show_bug.cgi?id=13203 + if (!stream || WebNetscapePluginStream::ownerForStream(stream) != plugin) { + LOG(Plugins, "Invalid NPStream passed to NPN_DestroyStream: %p", stream); + return NPERR_INVALID_INSTANCE_ERROR; + } + + WebNetscapePluginStream* browserStream = static_cast<WebNetscapePluginStream*>(stream->ndata); + browserStream->cancelLoadAndDestroyStreamWithError(browserStream->errorForReason(reason)); + + return NPERR_NO_ERROR; +} + +- (const char *)userAgent +{ + NSString *userAgent = [[self webView] userAgentForURL:_baseURL.get()]; + + if (_isSilverlight) { + // Silverlight has a workaround for a leak in Safari 2. This workaround is + // applied when the user agent does not contain "Version/3" so we append it + // at the end of the user agent. + userAgent = [userAgent stringByAppendingString:@" Version/3.2.1"]; + } + + return [userAgent UTF8String]; +} + +-(void)status:(const char *)message +{ + CFStringRef status = CFStringCreateWithCString(NULL, message ? message : "", kCFStringEncodingUTF8); + if (!status) { + LOG_ERROR("NPN_Status: the message was not valid UTF-8"); + return; + } + + LOG(Plugins, "NPN_Status: %@", status); + WebView *wv = [self webView]; + [[wv _UIDelegateForwarder] webView:wv setStatusText:(NSString *)status]; + CFRelease(status); +} + +-(void)invalidateRect:(NPRect *)invalidRect +{ + LOG(Plugins, "NPN_InvalidateRect"); + [self invalidatePluginContentRect:NSMakeRect(invalidRect->left, invalidRect->top, + (float)invalidRect->right - invalidRect->left, (float)invalidRect->bottom - invalidRect->top)]; +} + +- (void)invalidateRegion:(NPRegion)invalidRegion +{ + LOG(Plugins, "NPN_InvalidateRegion"); + NSRect invalidRect = NSZeroRect; + switch (drawingModel) { +#ifndef NP_NO_QUICKDRAW + case NPDrawingModelQuickDraw: + { + ::Rect qdRect; + GetRegionBounds((NPQDRegion)invalidRegion, &qdRect); + invalidRect = NSMakeRect(qdRect.left, qdRect.top, qdRect.right - qdRect.left, qdRect.bottom - qdRect.top); + } + break; +#endif /* NP_NO_QUICKDRAW */ + + case NPDrawingModelCoreGraphics: + { + CGRect cgRect = CGPathGetBoundingBox((NPCGRegion)invalidRegion); + invalidRect = *(NSRect*)&cgRect; + break; + } + default: + ASSERT_NOT_REACHED(); + break; + } + + [self invalidatePluginContentRect:invalidRect]; +} + +-(void)forceRedraw +{ + LOG(Plugins, "forceRedraw"); + [self invalidatePluginContentRect:[self bounds]]; + [[self window] displayIfNeeded]; +} + +- (NPError)getVariable:(NPNVariable)variable value:(void *)value +{ + switch (variable) { + case NPNVWindowNPObject: + { + Frame* frame = core([self webFrame]); + NPObject* windowScriptObject = frame ? frame->script()->windowScriptNPObject() : 0; + + // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess> + if (windowScriptObject) + _NPN_RetainObject(windowScriptObject); + + void **v = (void **)value; + *v = windowScriptObject; + + return NPERR_NO_ERROR; + } + + case NPNVPluginElementNPObject: + { + if (!_element) + return NPERR_GENERIC_ERROR; + + NPObject *plugInScriptObject = _element->getNPObject(); + + // Return value is expected to be retained, as described here: <http://www.mozilla.org/projects/plugins/npruntime.html#browseraccess> + if (plugInScriptObject) + _NPN_RetainObject(plugInScriptObject); + + void **v = (void **)value; + *v = plugInScriptObject; + + return NPERR_NO_ERROR; + } + + case NPNVpluginDrawingModel: + { + *(NPDrawingModel *)value = drawingModel; + return NPERR_NO_ERROR; + } + +#ifndef NP_NO_QUICKDRAW + case NPNVsupportsQuickDrawBool: + { + *(NPBool *)value = TRUE; + return NPERR_NO_ERROR; + } +#endif /* NP_NO_QUICKDRAW */ + + case NPNVsupportsCoreGraphicsBool: + { + *(NPBool *)value = TRUE; + return NPERR_NO_ERROR; + } + + case NPNVsupportsOpenGLBool: + { + *(NPBool *)value = FALSE; + return NPERR_NO_ERROR; + } + + case NPNVsupportsCoreAnimationBool: + { +#ifdef BUILDING_ON_TIGER + *(NPBool *)value = FALSE; +#else + *(NPBool *)value = TRUE; +#endif + return NPERR_NO_ERROR; + } + +#ifndef NP_NO_CARBON + case NPNVsupportsCarbonBool: + { + *(NPBool *)value = TRUE; + return NPERR_NO_ERROR; + } +#endif /* NP_NO_CARBON */ + + case NPNVsupportsCocoaBool: + { + *(NPBool *)value = TRUE; + return NPERR_NO_ERROR; + } + + case NPNVprivateModeBool: + { + *(NPBool *)value = _isPrivateBrowsingEnabled; + return NPERR_NO_ERROR; + } + + case WKNVBrowserContainerCheckFuncs: + { + *(WKNBrowserContainerCheckFuncs **)value = browserContainerCheckFuncs(); + return NPERR_NO_ERROR; + } +#if USE(ACCELERATED_COMPOSITING) + case WKNVSupportsCompositingCoreAnimationPluginsBool: + { + *(NPBool *)value = [[[self webView] preferences] acceleratedCompositingEnabled]; + return NPERR_NO_ERROR; + } +#endif + default: + break; + } + + return NPERR_GENERIC_ERROR; +} + +- (NPError)setVariable:(NPPVariable)variable value:(void *)value +{ + switch (variable) { + case NPPVpluginDrawingModel: + { + // Can only set drawing model inside NPP_New() + if (self != [[self class] currentPluginView]) + return NPERR_GENERIC_ERROR; + + // Check for valid, supported drawing model + NPDrawingModel newDrawingModel = (NPDrawingModel)(uintptr_t)value; + switch (newDrawingModel) { + // Supported drawing models: +#ifndef NP_NO_QUICKDRAW + case NPDrawingModelQuickDraw: +#endif + case NPDrawingModelCoreGraphics: +#ifndef BUILDING_ON_TIGER + case NPDrawingModelCoreAnimation: +#endif + drawingModel = newDrawingModel; + return NPERR_NO_ERROR; + + + // Unsupported (or unknown) drawing models: + default: + LOG(Plugins, "Plugin %@ uses unsupported drawing model: %d", _eventHandler.get(), drawingModel); + return NPERR_GENERIC_ERROR; + } + } + + case NPPVpluginEventModel: + { + // Can only set event model inside NPP_New() + if (self != [[self class] currentPluginView]) + return NPERR_GENERIC_ERROR; + + // Check for valid, supported event model + NPEventModel newEventModel = (NPEventModel)(uintptr_t)value; + switch (newEventModel) { + // Supported event models: +#ifndef NP_NO_CARBON + case NPEventModelCarbon: +#endif + case NPEventModelCocoa: + eventModel = newEventModel; + return NPERR_NO_ERROR; + + // Unsupported (or unknown) event models: + default: + LOG(Plugins, "Plugin %@ uses unsupported event model: %d", _eventHandler.get(), eventModel); + return NPERR_GENERIC_ERROR; + } + } + + default: + return NPERR_GENERIC_ERROR; + } +} + +- (uint32_t)scheduleTimerWithInterval:(uint32_t)interval repeat:(NPBool)repeat timerFunc:(void (*)(NPP npp, uint32_t timerID))timerFunc +{ + if (!timerFunc) + return 0; + + if (!timers) + timers = new HashMap<uint32_t, PluginTimer*>; + + uint32_t timerID; + + do { + timerID = ++currentTimerID; + } while (timers->contains(timerID) || timerID == 0); + + PluginTimer* timer = new PluginTimer(plugin, timerID, interval, repeat, timerFunc); + timers->set(timerID, timer); + + if (_shouldFireTimers) + timer->start(_isCompletelyObscured); + + return timerID; +} + +- (void)unscheduleTimer:(uint32_t)timerID +{ + if (!timers) + return; + + if (PluginTimer* timer = timers->take(timerID)) + delete timer; +} + +- (NPError)popUpContextMenu:(NPMenu *)menu +{ + NSEvent *currentEvent = [NSApp currentEvent]; + + // NPN_PopUpContextMenu must be called from within the plug-in's NPP_HandleEvent. + if (!currentEvent) + return NPERR_GENERIC_ERROR; + + [NSMenu popUpContextMenu:(NSMenu *)menu withEvent:currentEvent forView:self]; + return NPERR_NO_ERROR; +} + +- (NPError)getVariable:(NPNURLVariable)variable forURL:(const char*)url value:(char**)value length:(uint32_t*)length +{ + switch (variable) { + case NPNURLVCookie: { + if (!value) + break; + + NSURL *URL = [self URLWithCString:url]; + if (!URL) + break; + + if (Frame* frame = core([self webFrame])) { + String cookieString = cookies(frame->document(), URL); + CString cookieStringUTF8 = cookieString.utf8(); + if (cookieStringUTF8.isNull()) + return NPERR_GENERIC_ERROR; + + *value = static_cast<char*>(NPN_MemAlloc(cookieStringUTF8.length())); + memcpy(*value, cookieStringUTF8.data(), cookieStringUTF8.length()); + + if (length) + *length = cookieStringUTF8.length(); + return NPERR_NO_ERROR; + } + break; + } + case NPNURLVProxy: { +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + if (!value) + break; + + NSURL *URL = [self URLWithCString:url]; + if (!URL) + break; + + Vector<ProxyServer> proxyServers = proxyServersForURL(URL, 0); + CString proxiesUTF8 = toString(proxyServers).utf8(); + + *value = static_cast<char*>(NPN_MemAlloc(proxiesUTF8.length())); + memcpy(*value, proxiesUTF8.data(), proxiesUTF8.length()); + + if (length) + *length = proxiesUTF8.length(); + + return NPERR_NO_ERROR; +#else + break; +#endif + } + } + return NPERR_GENERIC_ERROR; +} + +- (NPError)setVariable:(NPNURLVariable)variable forURL:(const char*)url value:(const char*)value length:(uint32_t)length +{ + switch (variable) { + case NPNURLVCookie: { + NSURL *URL = [self URLWithCString:url]; + if (!URL) + break; + + String cookieString = String::fromUTF8(value, length); + if (!cookieString) + break; + + if (Frame* frame = core([self webFrame])) { + setCookies(frame->document(), URL, cookieString); + return NPERR_NO_ERROR; + } + + break; + } + case NPNURLVProxy: + // Can't set the proxy for a URL. + break; + } + return NPERR_GENERIC_ERROR; +} + +- (NPError)getAuthenticationInfoWithProtocol:(const char*)protocolStr host:(const char*)hostStr port:(int32_t)port scheme:(const char*)schemeStr realm:(const char*)realmStr + username:(char**)usernameStr usernameLength:(uint32_t*)usernameLength + password:(char**)passwordStr passwordLength:(uint32_t*)passwordLength +{ + if (!protocolStr || !hostStr || !schemeStr || !realmStr || !usernameStr || !usernameLength || !passwordStr || !passwordLength) + return NPERR_GENERIC_ERROR; + + CString username; + CString password; + if (!getAuthenticationInfo(protocolStr, hostStr, port, schemeStr, realmStr, username, password)) + return NPERR_GENERIC_ERROR; + + *usernameLength = username.length(); + *usernameStr = static_cast<char*>(NPN_MemAlloc(username.length())); + memcpy(*usernameStr, username.data(), username.length()); + + *passwordLength = password.length(); + *passwordStr = static_cast<char*>(NPN_MemAlloc(password.length())); + memcpy(*passwordStr, password.data(), password.length()); + + return NPERR_NO_ERROR; +} + +- (char*)resolveURL:(const char*)url forTarget:(const char*)target +{ + CString location = [self resolvedURLStringForURL:url target:target]; + + if (location.isNull()) + return 0; + + // We use strdup here because the caller needs to free it with NPN_MemFree (which calls free). + return strdup(location.data()); +} + +@end + +@implementation WebNetscapePluginView (Internal) + +- (BOOL)_shouldCancelSrcStream +{ + ASSERT(_isStarted); + + // Check if we should cancel the load + NPBool cancelSrcStream = 0; + if ([_pluginPackage.get() pluginFuncs]->getvalue && + [_pluginPackage.get() pluginFuncs]->getvalue(plugin, NPPVpluginCancelSrcStream, &cancelSrcStream) == NPERR_NO_ERROR && cancelSrcStream) + return YES; + + return NO; +} + +// Work around Silverlight full screen performance issue by maintaining an accelerated GL pixel format. +// We can safely remove it at some point in the future when both: +// 1) Microsoft releases a genuine fix for 7288546. +// 2) Enough Silverlight users update to the new Silverlight. +// For now, we'll distinguish older broken versions of Silverlight by asking the plug-in if it resolved its full screen badness. +- (void)_workaroundSilverlightFullscreenBug:(BOOL)initializedPlugin +{ +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + ASSERT(_isSilverlight); + NPBool isFullscreenPerformanceIssueFixed = 0; + NPPluginFuncs *pluginFuncs = [_pluginPackage.get() pluginFuncs]; + if (pluginFuncs->getvalue && pluginFuncs->getvalue(plugin, static_cast<NPPVariable>(WKNVSilverlightFullscreenPerformanceIssueFixed), &isFullscreenPerformanceIssueFixed) == NPERR_NO_ERROR && isFullscreenPerformanceIssueFixed) + return; + + static CGLPixelFormatObj pixelFormatObject = 0; + static unsigned refCount = 0; + + if (initializedPlugin) { + refCount++; + if (refCount == 1) { + const CGLPixelFormatAttribute attributes[] = { kCGLPFAAccelerated, static_cast<CGLPixelFormatAttribute>(0) }; + GLint npix; + CGLChoosePixelFormat(attributes, &pixelFormatObject, &npix); + } + } else { + ASSERT(pixelFormatObject); + refCount--; + if (!refCount) + CGLReleasePixelFormat(pixelFormatObject); + } +#endif +} + +- (NPError)_createPlugin +{ + plugin = (NPP)calloc(1, sizeof(NPP_t)); + plugin->ndata = self; + + ASSERT([_pluginPackage.get() pluginFuncs]->newp); + + // NPN_New(), which creates the plug-in instance, should never be called while calling a plug-in function for that instance. + ASSERT(pluginFunctionCallDepth == 0); + + PluginMainThreadScheduler::scheduler().registerPlugin(plugin); + + _isFlash = [_pluginPackage.get() bundleIdentifier] == "com.macromedia.Flash Player.plugin"; + _isSilverlight = [_pluginPackage.get() bundleIdentifier] == "com.microsoft.SilverlightPlugin"; + + [[self class] setCurrentPluginView:self]; + NPError npErr = [_pluginPackage.get() pluginFuncs]->newp((char *)[_MIMEType.get() cString], plugin, _mode, argsCount, cAttributes, cValues, NULL); + [[self class] setCurrentPluginView:nil]; + if (_isSilverlight) + [self _workaroundSilverlightFullscreenBug:YES]; + LOG(Plugins, "NPP_New: %d", npErr); + return npErr; +} + +- (void)_destroyPlugin +{ + PluginMainThreadScheduler::scheduler().unregisterPlugin(plugin); + + if (_isSilverlight) + [self _workaroundSilverlightFullscreenBug:NO]; + + NPError npErr; + npErr = ![_pluginPackage.get() pluginFuncs]->destroy(plugin, NULL); + LOG(Plugins, "NPP_Destroy: %d", npErr); + + if (Frame* frame = core([self webFrame])) + frame->script()->cleanupScriptObjectsForPlugin(self); + + free(plugin); + plugin = NULL; +} + +- (NSBitmapImageRep *)_printedPluginBitmap +{ +#ifdef NP_NO_QUICKDRAW + return nil; +#else + // Cannot print plugins that do not implement NPP_Print + if (![_pluginPackage.get() pluginFuncs]->print) + return nil; + + // This NSBitmapImageRep will share its bitmap buffer with a GWorld that the plugin will draw into. + // The bitmap is created in 32-bits-per-pixel ARGB format, which is the default GWorld pixel format. + NSBitmapImageRep *bitmap = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL + pixelsWide:window.width + pixelsHigh:window.height + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bitmapFormat:NSAlphaFirstBitmapFormat + bytesPerRow:0 + bitsPerPixel:0] autorelease]; + ASSERT(bitmap); + + // Create a GWorld with the same underlying buffer into which the plugin can draw + ::Rect printGWorldBounds; + SetRect(&printGWorldBounds, 0, 0, window.width, window.height); + GWorldPtr printGWorld; + if (NewGWorldFromPtr(&printGWorld, + k32ARGBPixelFormat, + &printGWorldBounds, + NULL, + NULL, + 0, + (Ptr)[bitmap bitmapData], + [bitmap bytesPerRow]) != noErr) { + LOG_ERROR("Could not create GWorld for printing"); + return nil; + } + + /// Create NPWindow for the GWorld + NPWindow printNPWindow; + printNPWindow.window = &printGWorld; // Normally this is an NP_Port, but when printing it is the actual CGrafPtr + printNPWindow.x = 0; + printNPWindow.y = 0; + printNPWindow.width = window.width; + printNPWindow.height = window.height; + printNPWindow.clipRect.top = 0; + printNPWindow.clipRect.left = 0; + printNPWindow.clipRect.right = window.width; + printNPWindow.clipRect.bottom = window.height; + printNPWindow.type = NPWindowTypeDrawable; // Offscreen graphics port as opposed to a proper window + + // Create embed-mode NPPrint + NPPrint npPrint; + npPrint.mode = NP_EMBED; + npPrint.print.embedPrint.window = printNPWindow; + npPrint.print.embedPrint.platformPrint = printGWorld; + + // Tell the plugin to print into the GWorld + [self willCallPlugInFunction]; + { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + [_pluginPackage.get() pluginFuncs]->print(plugin, &npPrint); + } + [self didCallPlugInFunction]; + + // Don't need the GWorld anymore + DisposeGWorld(printGWorld); + + return bitmap; +#endif +} + +- (void)_redeliverStream +{ + if ([self dataSource] && _isStarted) { + // Deliver what has not been passed to the plug-in up to this point. + if (_dataLengthReceived > 0) { + NSData *data = [[[self dataSource] data] subdataWithRange:NSMakeRange(0, _dataLengthReceived)]; + _dataLengthReceived = 0; + [self pluginView:self receivedData:data]; + if (![[self dataSource] isLoading]) { + if (_error) + [self pluginView:self receivedError:_error.get()]; + else + [self pluginViewFinishedLoading:self]; + } + } + } +} + +@end + +#endif diff --git a/Source/WebKit/mac/Plugins/WebPlugin.h b/Source/WebKit/mac/Plugins/WebPlugin.h new file mode 100644 index 0000000..797634f --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebPlugin.h @@ -0,0 +1,132 @@ +/* + * 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. + * 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> + +/*! + WebPlugIn is an informal protocol that enables interaction between an application + and web related plug-ins it may contain. +*/ + +@interface NSObject (WebPlugIn) + +/*! + @method webPlugInInitialize + @abstract Tell the plug-in to perform one-time initialization. + @discussion This method must be only called once per instance of the plug-in + object and must be called before any other methods in this protocol. +*/ +- (void)webPlugInInitialize; + +/*! + @method webPlugInStart + @abstract Tell the plug-in to start normal operation. + @discussion The plug-in usually begins drawing, playing sounds and/or + animation in this method. This method must be called before calling webPlugInStop. + This method may called more than once, provided that the application has + already called webPlugInInitialize and that each call to webPlugInStart is followed + by a call to webPlugInStop. +*/ +- (void)webPlugInStart; + +/*! + @method webPlugInStop + @abstract Tell the plug-in to stop normal operation. + @discussion webPlugInStop must be called before this method. This method may be + called more than once, provided that the application has already called + webPlugInInitialize and that each call to webPlugInStop is preceded by a call to + webPlugInStart. +*/ +- (void)webPlugInStop; + +/*! + @method webPlugInDestroy + @abstract Tell the plug-in perform cleanup and prepare to be deallocated. + @discussion The plug-in typically releases memory and other resources in this + method. If the plug-in has retained the WebPlugInContainer, it must release + it in this mehthod. This method must be only called once per instance of the + plug-in object. No other methods in this interface may be called after the + application has called webPlugInDestroy. +*/ +- (void)webPlugInDestroy; + +/*! + @method webPlugInSetIsSelected: + @discusssion Informs the plug-in whether or not it is selected. This is typically + used to allow the plug-in to alter it's appearance when selected. +*/ +- (void)webPlugInSetIsSelected:(BOOL)isSelected; + +/*! + @method objectForWebScript + @discussion objectForWebScript is used to expose a plug-in's scripting interface. The + methods of the object are exposed to the script environment. See the WebScripting + informal protocol for more details. + @result Returns the object that exposes the plug-in's interface. The class of this + object can implement methods from the WebScripting informal protocol. +*/ +- (id)objectForWebScript; + +/*! + @method webPlugInMainResourceDidReceiveResponse: + @abstract Called on the plug-in when WebKit receives -connection:didReceiveResponse: + for the plug-in's main resource. + @discussion This method is only sent to the plug-in if the + WebPlugInShouldLoadMainResourceKey argument passed to the plug-in was NO. +*/ +- (void)webPlugInMainResourceDidReceiveResponse:(NSURLResponse *)response WEBKIT_OBJC_METHOD_ANNOTATION(AVAILABLE_IN_WEBKIT_VERSION_4_0); + +/*! + @method webPlugInMainResourceDidReceiveData: + @abstract Called on the plug-in when WebKit recieves -connection:didReceiveData: + for the plug-in's main resource. + @discussion This method is only sent to the plug-in if the + WebPlugInShouldLoadMainResourceKey argument passed to the plug-in was NO. +*/ +- (void)webPlugInMainResourceDidReceiveData:(NSData *)data WEBKIT_OBJC_METHOD_ANNOTATION(AVAILABLE_IN_WEBKIT_VERSION_4_0); + +/*! + @method webPlugInMainResourceDidFailWithError: + @abstract Called on the plug-in when WebKit receives -connection:didFailWithError: + for the plug-in's main resource. + @discussion This method is only sent to the plug-in if the + WebPlugInShouldLoadMainResourceKey argument passed to the plug-in was NO. +*/ +- (void)webPlugInMainResourceDidFailWithError:(NSError *)error WEBKIT_OBJC_METHOD_ANNOTATION(AVAILABLE_IN_WEBKIT_VERSION_4_0); + +/*! + @method webPlugInMainResourceDidFinishLoading + @abstract Called on the plug-in when WebKit receives -connectionDidFinishLoading: + for the plug-in's main resource. + @discussion This method is only sent to the plug-in if the + WebPlugInShouldLoadMainResourceKey argument passed to the plug-in was NO. +*/ +- (void)webPlugInMainResourceDidFinishLoading WEBKIT_OBJC_METHOD_ANNOTATION(AVAILABLE_IN_WEBKIT_VERSION_4_0); + +@end diff --git a/Source/WebKit/mac/Plugins/WebPluginContainer.h b/Source/WebKit/mac/Plugins/WebPluginContainer.h new file mode 100644 index 0000000..112b677 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebPluginContainer.h @@ -0,0 +1,72 @@ +/* + * 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. + * 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> + +/*! + This informal protocol enables a plug-in to request that its containing application + perform certain operations. +*/ + +@interface NSObject (WebPlugInContainer) + +/*! + @method webPlugInContainerLoadRequest:inFrame: + @abstract Tell the application to show a URL in a target frame + @param request The request to be loaded. + @param target The target frame. If the frame with the specified target is not + found, a new window is opened and the main frame of the new window is named + with the specified target. If nil is specified, the frame that contains + the applet is targeted. +*/ +- (void)webPlugInContainerLoadRequest:(NSURLRequest *)request inFrame:(NSString *)target; + +/*! + @method webPlugInContainerShowStatus: + @abstract Tell the application to show the specified status message. + @param message The string to be shown. +*/ +- (void)webPlugInContainerShowStatus:(NSString *)message; + +/*! + @method webPlugInContainerSelectionColor + @result Returns the color that should be used for any special drawing when + plug-in is selected. +*/ +- (NSColor *)webPlugInContainerSelectionColor; + +/*! + @method webFrame + @discussion The webFrame method allows the plug-in to access the WebFrame that + contains the plug-in. This method will not be implemented by containers that + are not WebKit based. + @result Return the WebFrame that contains the plug-in. +*/ +- (WebFrame *)webFrame; + +@end diff --git a/Source/WebKit/mac/Plugins/WebPluginContainerCheck.h b/Source/WebKit/mac/Plugins/WebPluginContainerCheck.h new file mode 100644 index 0000000..419be51 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebPluginContainerCheck.h @@ -0,0 +1,60 @@ +/* + * 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 NSURLRequest; +@class NSString; +@class WebFrame; +@class WebView; +@class WebPolicyDecisionListener; + +@protocol WebPluginContainerCheckController <NSObject> +- (void)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)checkIdentifier; +- (WebFrame *)webFrame; +- (WebView *)webView; +@end + +@interface WebPluginContainerCheck : NSObject +{ + NSURLRequest *_request; + NSString *_target; + id <WebPluginContainerCheckController> _controller; + id _resultObject; + SEL _resultSelector; + id _contextInfo; + BOOL _done; + WebPolicyDecisionListener *_listener; +} + ++ (id)checkWithRequest:(NSURLRequest *)request target:(NSString *)target resultObject:(id)obj selector:(SEL)selector controller:(id <WebPluginContainerCheckController>)controller contextInfo:(id)/*optional*/contextInfo; +- (void)start; +- (void)cancel; +- (id)contextInfo; + +@end diff --git a/Source/WebKit/mac/Plugins/WebPluginContainerCheck.mm b/Source/WebKit/mac/Plugins/WebPluginContainerCheck.mm new file mode 100644 index 0000000..30368a8 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebPluginContainerCheck.mm @@ -0,0 +1,193 @@ +/* + * 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 "WebPluginContainerCheck.h" + +#import "WebFrameInternal.h" +#import "WebPluginContainerPrivate.h" +#import "WebPluginController.h" +#import "WebPolicyDelegatePrivate.h" +#import "WebView.h" +#import "WebViewInternal.h" +#import <Foundation/NSDictionary.h> +#import <Foundation/NSURL.h> +#import <Foundation/NSURLRequest.h> +#import <WebCore/Frame.h> +#import <WebCore/FrameLoader.h> +#import <WebCore/FrameLoaderTypes.h> +#import <WebCore/SecurityOrigin.h> +#import <wtf/Assertions.h> +#import <objc/objc-runtime.h> + +using namespace WebCore; + +@implementation WebPluginContainerCheck + +- (id)initWithRequest:(NSURLRequest *)request target:(NSString *)target resultObject:(id)obj selector:(SEL)selector controller:(id <WebPluginContainerCheckController>)controller contextInfo:(id)contextInfo /*optional*/ +{ + if (!(self = [super init])) + return nil; + + _request = [request copy]; + _target = [target copy]; + _resultObject = [obj retain]; + _resultSelector = selector; + _contextInfo = [contextInfo retain]; + + // controller owns us so don't retain, to avoid cycle + _controller = controller; + + return self; +} + ++ (id)checkWithRequest:(NSURLRequest *)request target:(NSString *)target resultObject:(id)obj selector:(SEL)selector controller:(id <WebPluginContainerCheckController>)controller contextInfo:(id)contextInfo /*optional*/ +{ + return [[[self alloc] initWithRequest:request target:target resultObject:obj selector:selector controller:controller contextInfo:contextInfo] autorelease]; +} + +- (void)finalize +{ + // mandatory to complete or cancel before releasing this object + ASSERT(_done); + [super finalize]; +} + +- (void)dealloc +{ + // mandatory to complete or cancel before releasing this object + ASSERT(_done); + [super dealloc]; +} + +- (void)_continueWithPolicy:(PolicyAction)policy +{ + if (_contextInfo) + ((void (*)(id, SEL, BOOL, id))objc_msgSend)(_resultObject, _resultSelector, (policy == PolicyUse), _contextInfo); + else + ((void (*)(id, SEL, BOOL))objc_msgSend)(_resultObject, _resultSelector, (policy == PolicyUse)); + + // this will call indirectly call cancel + [_controller _webPluginContainerCancelCheckIfAllowedToLoadRequest:self]; +} + +- (BOOL)_isForbiddenFileLoad +{ + Frame* coreFrame = core([_controller webFrame]); + ASSERT(coreFrame); + if (!coreFrame->document()->securityOrigin()->canDisplay([_request URL])) { + [self _continueWithPolicy:PolicyIgnore]; + return YES; + } + + return NO; +} + +- (NSDictionary *)_actionInformationWithURL:(NSURL *)URL +{ + return [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt:WebNavigationTypePlugInRequest], WebActionNavigationTypeKey, + [NSNumber numberWithInt:0], WebActionModifierFlagsKey, + URL, WebActionOriginalURLKey, + nil]; +} + +- (void)_askPolicyDelegate +{ + WebView *webView = [_controller webView]; + + WebFrame *targetFrame; + if ([_target length] > 0) { + targetFrame = [[_controller webFrame] findFrameNamed:_target]; + } else { + targetFrame = [_controller webFrame]; + } + + NSDictionary *action = [self _actionInformationWithURL:[_request URL]]; + + _listener = [[WebPolicyDecisionListener alloc] _initWithTarget:self action:@selector(_continueWithPolicy:)]; + + if (targetFrame == nil) { + // would open new window + [[webView _policyDelegateForwarder] webView:webView + decidePolicyForNewWindowAction:action + request:_request + newFrameName:_target + decisionListener:_listener]; + } else { + // would target existing frame + [[webView _policyDelegateForwarder] webView:webView + decidePolicyForNavigationAction:action + request:_request + frame:targetFrame + decisionListener:_listener]; + } +} + +- (void)start +{ + ASSERT(!_listener); + ASSERT(!_done); + + if ([self _isForbiddenFileLoad]) + return; + + [self _askPolicyDelegate]; +} + +- (void)cancel +{ + if (_done) + return; + + [_request release]; + _request = nil; + + [_target release]; + _target = nil; + + [_listener _invalidate]; + [_listener release]; + _listener = nil; + + [_resultObject autorelease]; + _resultObject = nil; + + _controller = nil; + + [_contextInfo release]; + _contextInfo = nil; + + _done = YES; +} + +- (id)contextInfo +{ + return _contextInfo; +} + +@end diff --git a/Source/WebKit/mac/Plugins/WebPluginContainerPrivate.h b/Source/WebKit/mac/Plugins/WebPluginContainerPrivate.h new file mode 100644 index 0000000..1b8de41 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebPluginContainerPrivate.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 <Cocoa/Cocoa.h> + +#if !defined(ENABLE_PLUGIN_PROXY_FOR_VIDEO) +#define ENABLE_PLUGIN_PROXY_FOR_VIDEO 0 +#endif + +#if ENABLE_PLUGIN_PROXY_FOR_VIDEO +@class WebMediaPlayerProxy; +#endif + +@interface NSObject (WebPlugInContainerPrivate) + +- (id)_webPluginContainerCheckIfAllowedToLoadRequest:(NSURLRequest *)Request inFrame:(NSString *)target resultObject:(id)obj selector:(SEL)selector; + +- (void)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)checkIdentifier; + +#if ENABLE_PLUGIN_PROXY_FOR_VIDEO +- (void)_webPluginContainerSetMediaPlayerProxy:(WebMediaPlayerProxy *)proxy forElement:(DOMElement *)element; + +- (void)_webPluginContainerPostMediaPlayerNotification:(int)notification forElement:(DOMElement *)element; +#endif + +@end diff --git a/Source/WebKit/mac/Plugins/WebPluginController.h b/Source/WebKit/mac/Plugins/WebPluginController.h new file mode 100644 index 0000000..dc1f413 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebPluginController.h @@ -0,0 +1,73 @@ +/* + * 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/WebBasePluginPackage.h> +#import <WebKit/WebPluginContainerCheck.h> + +@class WebFrame; +@class WebHTMLView; +@class WebPluginPackage; +@class WebView; +@class WebDataSource; + +@interface WebPluginController : NSObject <WebPluginManualLoader, WebPluginContainerCheckController> +{ + NSView *_documentView; + WebDataSource *_dataSource; + NSMutableArray *_views; + BOOL _started; + NSMutableSet *_checksInProgress; +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + NSMutableArray *_viewsNotInDocument; +#endif +} + ++ (NSView *)plugInViewWithArguments:(NSDictionary *)arguments fromPluginPackage:(WebPluginPackage *)plugin; ++ (BOOL)isPlugInView:(NSView *)view; + +- (id)initWithDocumentView:(NSView *)view; + +- (void)setDataSource:(WebDataSource *)dataSource; + +- (void)addPlugin:(NSView *)view; +- (void)destroyPlugin:(NSView *)view; +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +- (void)pluginViewCreated:(NSView *)view; ++ (void)pluginViewHidden:(NSView *)view; +#endif + +- (void)startAllPlugins; +- (void)stopAllPlugins; +- (void)destroyAllPlugins; + +- (WebFrame *)webFrame; +- (WebView *)webView; + +- (NSString *)URLPolicyCheckReferrer; + +@end diff --git a/Source/WebKit/mac/Plugins/WebPluginController.mm b/Source/WebKit/mac/Plugins/WebPluginController.mm new file mode 100644 index 0000000..c802d4c --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebPluginController.mm @@ -0,0 +1,601 @@ +/* + * 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 "WebPluginController.h" + +#import "DOMNodeInternal.h" +#import "WebDataSourceInternal.h" +#import "WebFrameInternal.h" +#import "WebFrameView.h" +#import "WebHTMLViewPrivate.h" +#import "WebKitErrorsPrivate.h" +#import "WebKitLogging.h" +#import "WebNSObjectExtras.h" +#import "WebNSURLExtras.h" +#import "WebNSViewExtras.h" +#import "WebPlugin.h" +#import "WebPluginContainer.h" +#import "WebPluginContainerCheck.h" +#import "WebPluginPackage.h" +#import "WebPluginPrivate.h" +#import "WebPluginViewFactory.h" +#import "WebUIDelegate.h" +#import "WebViewInternal.h" +#import <Foundation/NSURLRequest.h> +#import <WebCore/DocumentLoader.h> +#import <WebCore/Frame.h> +#import <WebCore/FrameLoader.h> +#import <WebCore/HTMLMediaElement.h> +#import <WebCore/HTMLNames.h> +#import <WebCore/MediaPlayerProxy.h> +#import <WebCore/PlatformString.h> +#import <WebCore/ResourceRequest.h> +#import <WebCore/ScriptController.h> +#import <WebCore/WebCoreURLResponse.h> +#import <objc/objc-runtime.h> +#import <runtime/JSLock.h> + +using namespace WebCore; +using namespace HTMLNames; + +@interface NSView (PluginSecrets) +- (void)setContainingWindow:(NSWindow *)w; +@end + +// For compatibility only. +@interface NSObject (OldPluginAPI) ++ (NSView *)pluginViewWithArguments:(NSDictionary *)arguments; +@end + +@interface NSView (OldPluginAPI) +- (void)pluginInitialize; +- (void)pluginStart; +- (void)pluginStop; +- (void)pluginDestroy; +@end + +static bool isKindOfClass(id, NSString* className); +static void installFlip4MacPlugInWorkaroundIfNecessary(); + + +static NSMutableSet *pluginViews = nil; + +@implementation WebPluginController + ++ (NSView *)plugInViewWithArguments:(NSDictionary *)arguments fromPluginPackage:(WebPluginPackage *)pluginPackage +{ + [pluginPackage load]; + Class viewFactory = [pluginPackage viewFactory]; + + NSView *view = nil; + + if ([viewFactory respondsToSelector:@selector(plugInViewWithArguments:)]) { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + view = [viewFactory plugInViewWithArguments:arguments]; + } else if ([viewFactory respondsToSelector:@selector(pluginViewWithArguments:)]) { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + view = [viewFactory pluginViewWithArguments:arguments]; + } + + if (view == nil) { + return nil; + } + + if (pluginViews == nil) { + pluginViews = [[NSMutableSet alloc] init]; + } + [pluginViews addObject:view]; + + return view; +} + ++ (BOOL)isPlugInView:(NSView *)view +{ + return [pluginViews containsObject:view]; +} + +- (id)initWithDocumentView:(NSView *)view +{ + [super init]; + _documentView = view; + _views = [[NSMutableArray alloc] init]; + _checksInProgress = (NSMutableSet *)CFMakeCollectable(CFSetCreateMutable(NULL, 0, NULL)); + return self; +} + +- (void)setDataSource:(WebDataSource *)dataSource +{ + _dataSource = dataSource; +} + +- (void)dealloc +{ + [_views release]; + [_checksInProgress release]; +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + [_viewsNotInDocument release]; +#endif + [super dealloc]; +} + +- (void)stopOnePlugin:(NSView *)view +{ + if ([view respondsToSelector:@selector(webPlugInStop)]) { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + [view webPlugInStop]; + } else if ([view respondsToSelector:@selector(pluginStop)]) { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + [view pluginStop]; + } +} + +- (void)destroyOnePlugin:(NSView *)view +{ + if ([view respondsToSelector:@selector(webPlugInDestroy)]) { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + [view webPlugInDestroy]; + } else if ([view respondsToSelector:@selector(pluginDestroy)]) { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + [view pluginDestroy]; + } +} + +- (void)startAllPlugins +{ + if (_started) + return; + + if ([_views count] > 0) + LOG(Plugins, "starting WebKit plugins : %@", [_views description]); + + int count = [_views count]; + for (int i = 0; i < count; i++) { + id aView = [_views objectAtIndex:i]; + if ([aView respondsToSelector:@selector(webPlugInStart)]) { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + [aView webPlugInStart]; + } else if ([aView respondsToSelector:@selector(pluginStart)]) { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + [aView pluginStart]; + } + } + _started = YES; +} + +- (void)stopAllPlugins +{ + if (!_started) + return; + + if ([_views count] > 0) { + LOG(Plugins, "stopping WebKit plugins: %@", [_views description]); + } + + int viewsCount = [_views count]; + for (int i = 0; i < viewsCount; i++) + [self stopOnePlugin:[_views objectAtIndex:i]]; + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + int viewsNotInDocumentCount = [_viewsNotInDocument count]; + for (int i = 0; i < viewsNotInDocumentCount; i++) + [self stopOnePlugin:[_viewsNotInDocument objectAtIndex:i]]; +#endif + + _started = NO; +} + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +- (void)pluginViewCreated:(NSView *)view +{ + if (!_viewsNotInDocument) + _viewsNotInDocument= [[NSMutableArray alloc] init]; + if (![_viewsNotInDocument containsObject:view]) + [_viewsNotInDocument addObject:view]; +} + ++ (void)pluginViewHidden:(NSView *)view +{ + [pluginViews removeObject:view]; +} +#endif + +- (void)addPlugin:(NSView *)view +{ + if (!_documentView) { + LOG_ERROR("can't add a plug-in to a defunct WebPluginController"); + return; + } + + if (![_views containsObject:view]) { + [_views addObject:view]; + [[_documentView _webView] addPluginInstanceView:view]; + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + if ([_viewsNotInDocument containsObject:view]) + [_viewsNotInDocument removeObject:view]; +#endif + + BOOL oldDefersCallbacks = [[self webView] defersCallbacks]; + if (!oldDefersCallbacks) + [[self webView] setDefersCallbacks:YES]; + + if (isKindOfClass(view, @"WmvPlugin")) + installFlip4MacPlugInWorkaroundIfNecessary(); + + LOG(Plugins, "initializing plug-in %@", view); + if ([view respondsToSelector:@selector(webPlugInInitialize)]) { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + [view webPlugInInitialize]; + } else if ([view respondsToSelector:@selector(pluginInitialize)]) { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + [view pluginInitialize]; + } + + if (!oldDefersCallbacks) + [[self webView] setDefersCallbacks:NO]; + + if (_started) { + LOG(Plugins, "starting plug-in %@", view); + if ([view respondsToSelector:@selector(webPlugInStart)]) { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + [view webPlugInStart]; + } else if ([view respondsToSelector:@selector(pluginStart)]) { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + [view pluginStart]; + } + + if ([view respondsToSelector:@selector(setContainingWindow:)]) { + JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); + [view setContainingWindow:[_documentView window]]; + } + } + } +} + +- (void)destroyPlugin:(NSView *)view +{ +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + if ([_views containsObject:view] || [_viewsNotInDocument containsObject:view]) { +#else + if ([_views containsObject:view]) { +#endif + if (_started) + [self stopOnePlugin:view]; + [self destroyOnePlugin:view]; + +#if ENABLE(NETSCAPE_PLUGIN_API) + if (Frame* frame = core([self webFrame])) + frame->script()->cleanupScriptObjectsForPlugin(self); +#endif + + [pluginViews removeObject:view]; + [[_documentView _webView] removePluginInstanceView:view]; + [_views removeObject:view]; +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + [_viewsNotInDocument removeObject:view]; +#endif + } +} + +- (void)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)checkIdentifier +{ + [checkIdentifier cancel]; + [_checksInProgress removeObject:checkIdentifier]; +} + +static void cancelOutstandingCheck(const void *item, void *context) +{ + [(id)item cancel]; +} + +- (void)_cancelOutstandingChecks +{ + if (_checksInProgress) { + CFSetApplyFunction((CFSetRef)_checksInProgress, cancelOutstandingCheck, NULL); + [_checksInProgress release]; + _checksInProgress = nil; + } +} + +- (void)destroyAllPlugins +{ + [self stopAllPlugins]; + + if ([_views count] > 0) { + LOG(Plugins, "destroying WebKit plugins: %@", [_views description]); + } + + [self _cancelOutstandingChecks]; + + int viewsCount = [_views count]; + for (int i = 0; i < viewsCount; i++) { + id aView = [_views objectAtIndex:i]; + [self destroyOnePlugin:aView]; + +#if ENABLE(NETSCAPE_PLUGIN_API) + if (Frame* frame = core([self webFrame])) + frame->script()->cleanupScriptObjectsForPlugin(self); +#endif + + [pluginViews removeObject:aView]; + [[_documentView _webView] removePluginInstanceView:aView]; + } + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + int viewsNotInDocumentCount = [_viewsNotInDocument count]; + for (int i = 0; i < viewsNotInDocumentCount; i++) + [self destroyOnePlugin:[_viewsNotInDocument objectAtIndex:i]]; +#endif + + [_views makeObjectsPerformSelector:@selector(removeFromSuperviewWithoutNeedingDisplay)]; + [_views release]; + _views = nil; + + _documentView = nil; +} + +- (id)_webPluginContainerCheckIfAllowedToLoadRequest:(NSURLRequest *)request inFrame:(NSString *)target resultObject:(id)obj selector:(SEL)selector +{ + WebPluginContainerCheck *check = [WebPluginContainerCheck checkWithRequest:request target:target resultObject:obj selector:selector controller:self contextInfo:nil]; + [_checksInProgress addObject:check]; + [check start]; + + return check; +} + +- (void)webPlugInContainerLoadRequest:(NSURLRequest *)request inFrame:(NSString *)target +{ + if (!request) { + LOG_ERROR("nil URL passed"); + return; + } + if (!_documentView) { + LOG_ERROR("could not load URL %@ because plug-in has already been destroyed", request); + return; + } + WebFrame *frame = [_dataSource webFrame]; + if (!frame) { + LOG_ERROR("could not load URL %@ because plug-in has already been stopped", request); + return; + } + if (!target) { + target = @"_top"; + } + NSString *JSString = [[request URL] _webkit_scriptIfJavaScriptURL]; + if (JSString) { + if ([frame findFrameNamed:target] != frame) { + LOG_ERROR("JavaScript requests can only be made on the frame that contains the plug-in"); + return; + } + [frame _stringByEvaluatingJavaScriptFromString:JSString]; + } else { + if (!request) { + LOG_ERROR("could not load URL %@", [request URL]); + return; + } + core(frame)->loader()->load(request, target, false); + } +} + +- (void)webPlugInContainerShowStatus:(NSString *)message +{ + if (!message) + message = @""; + + WebView *v = [_dataSource _webView]; + [[v _UIDelegateForwarder] webView:v setStatusText:message]; +} + +// For compatibility only. +- (void)showStatus:(NSString *)message +{ + [self webPlugInContainerShowStatus:message]; +} + +- (NSColor *)webPlugInContainerSelectionColor +{ + bool primary = true; + if (Frame* frame = core([self webFrame])) + primary = frame->selection()->isFocusedAndActive(); + return primary ? [NSColor selectedTextBackgroundColor] : [NSColor secondarySelectedControlColor]; +} + +// For compatibility only. +- (NSColor *)selectionColor +{ + return [self webPlugInContainerSelectionColor]; +} + +- (WebFrame *)webFrame +{ + return [_dataSource webFrame]; +} + +- (WebView *)webView +{ + return [[self webFrame] webView]; +} + +- (NSString *)URLPolicyCheckReferrer +{ + NSURL *responseURL = [[[[self webFrame] _dataSource] response] URL]; + ASSERT(responseURL); + return [responseURL _web_originalDataAsString]; +} + +- (void)pluginView:(NSView *)pluginView receivedResponse:(NSURLResponse *)response +{ + if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidReceiveResponse:)]) + [pluginView webPlugInMainResourceDidReceiveResponse:response]; + else { + // Cancel the load since this plug-in does its own loading. + // FIXME: See <rdar://problem/4258008> for a problem with this. + NSError *error = [[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInWillHandleLoad + contentURL:[response URL] + pluginPageURL:nil + pluginName:nil // FIXME: Get this from somewhere + MIMEType:[response MIMEType]]; + [_dataSource _documentLoader]->cancelMainResourceLoad(error); + [error release]; + } +} + +- (void)pluginView:(NSView *)pluginView receivedData:(NSData *)data +{ + if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidReceiveData:)]) + [pluginView webPlugInMainResourceDidReceiveData:data]; +} + +- (void)pluginView:(NSView *)pluginView receivedError:(NSError *)error +{ + if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidFailWithError:)]) + [pluginView webPlugInMainResourceDidFailWithError:error]; +} + +- (void)pluginViewFinishedLoading:(NSView *)pluginView +{ + if ([pluginView respondsToSelector:@selector(webPlugInMainResourceDidFinishLoading)]) + [pluginView webPlugInMainResourceDidFinishLoading]; +} + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +static WebCore::HTMLMediaElement* mediaProxyClient(DOMElement* element) +{ + if (!element) { + LOG_ERROR("nil element passed"); + return nil; + } + + Element* node = core(element); + if (!node || (!node->hasTagName(HTMLNames::videoTag) && !node->hasTagName(HTMLNames::audioTag))) { + LOG_ERROR("invalid media element passed"); + return nil; + } + + return static_cast<WebCore::HTMLMediaElement*>(node); +} + +- (void)_webPluginContainerSetMediaPlayerProxy:(WebMediaPlayerProxy *)proxy forElement:(DOMElement *)element +{ + WebCore::HTMLMediaElement* client = mediaProxyClient(element); + if (client) + client->setMediaPlayerProxy(proxy); +} + +- (void)_webPluginContainerPostMediaPlayerNotification:(int)notification forElement:(DOMElement *)element +{ + WebCore::HTMLMediaElement* client = mediaProxyClient(element); + if (client) + client->deliverNotification((MediaPlayerProxyNotificationType)notification); +} +#endif + +@end + +static bool isKindOfClass(id object, NSString *className) +{ + Class cls = NSClassFromString(className); + + if (!cls) + return false; + + return [object isKindOfClass:cls]; +} + + +// Existing versions of the Flip4Mac WebKit plug-in have an object lifetime bug related to an NSAlert that is +// used to notify the user about updates to the plug-in. This bug can result in Safari crashing if the page +// containing the plug-in navigates while the alert is displayed (<rdar://problem/7313430>). +// +// The gist of the bug is thus: Flip4Mac sets an instance of the TSUpdateCheck class as the modal delegate of the +// NSAlert instance. This TSUpdateCheck instance itself has a delegate. The delegate is set to the WmvPlugin +// instance which is the NSView subclass that is exposed to WebKit as the plug-in view. Since this relationship +// is that of delegates the TSUpdateCheck does not retain the WmvPlugin. This leads to a bug if the WmvPlugin +// instance is destroyed before the TSUpdateCheck instance as the TSUpdateCheck instance will be left with a +// pointer to a stale object. This will happen if a page containing the Flip4Mac plug-in triggers a navigation +// while the update sheet is visible as the WmvPlugin instance is removed from the view hierarchy and there are +// no other references to keep the object alive. +// +// We work around this bug by patching the following two messages: +// +// 1) -[NSAlert beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo:] +// 2) -[TSUpdateCheck alertDidEnd:returnCode:contextInfo:] +// +// Our override of 1) detects whether it is Flip4Mac's update sheet triggering the alert by checking whether the +// modal delegate is an instance of TSUpdateCheck. If it is, it retains the modal delegate's delegate. +// +// Our override of 2) then autoreleases the delegate, balancing the retain we added in 1). +// +// These two overrides have the effect of ensuring that the WmvPlugin instance will always outlive the TSUpdateCheck +// instance, preventing the TSUpdateCheck instance from accessing a stale delegate pointer and crashing the application. + + +typedef void (*beginSheetModalForWindowIMP)(id, SEL, NSWindow *, id, SEL, void*); +static beginSheetModalForWindowIMP original_NSAlert_beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_; + +typedef void (*alertDidEndIMP)(id, SEL, NSAlert *, NSInteger, void*); +static alertDidEndIMP original_TSUpdateCheck_alertDidEnd_returnCode_contextInfo_; + +static void WebKit_TSUpdateCheck_alertDidEnd_returnCode_contextInfo_(id object, SEL selector, NSAlert *alert, NSInteger returnCode, void* contextInfo) +{ + [[object delegate] autorelease]; + + original_TSUpdateCheck_alertDidEnd_returnCode_contextInfo_(object, selector, alert, returnCode, contextInfo); +} + +static void WebKit_NSAlert_beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(id object, SEL selector, NSWindow *window, id modalDelegate, SEL didEndSelector, void* contextInfo) +{ + if (isKindOfClass(modalDelegate, @"TSUpdateCheck")) + [[modalDelegate delegate] retain]; + + original_NSAlert_beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(object, selector, window, modalDelegate, didEndSelector, contextInfo); +} + +static void installFlip4MacPlugInWorkaroundIfNecessary() +{ + static bool hasInstalledFlip4MacPlugInWorkaround; + if (!hasInstalledFlip4MacPlugInWorkaround) { + Class TSUpdateCheck = objc_lookUpClass("TSUpdateCheck"); + if (!TSUpdateCheck) + return; + + Method methodToPatch = class_getInstanceMethod(TSUpdateCheck, @selector(alertDidEnd:returnCode:contextInfo:)); + if (!methodToPatch) + return; + + IMP originalMethod = method_setImplementation(methodToPatch, reinterpret_cast<IMP>(WebKit_TSUpdateCheck_alertDidEnd_returnCode_contextInfo_)); + original_TSUpdateCheck_alertDidEnd_returnCode_contextInfo_ = reinterpret_cast<alertDidEndIMP>(originalMethod); + + methodToPatch = class_getInstanceMethod(objc_getRequiredClass("NSAlert"), @selector(beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo:)); + originalMethod = method_setImplementation(methodToPatch, reinterpret_cast<IMP>(WebKit_NSAlert_beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_)); + original_NSAlert_beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_ = reinterpret_cast<beginSheetModalForWindowIMP>(originalMethod); + + hasInstalledFlip4MacPlugInWorkaround = true; + } +} diff --git a/Source/WebKit/mac/Plugins/WebPluginDatabase.h b/Source/WebKit/mac/Plugins/WebPluginDatabase.h new file mode 100644 index 0000000..350f0a7 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebPluginDatabase.h @@ -0,0 +1,71 @@ +/* + * 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 WebBasePluginPackage; +@class WebFrame; + +@interface WebPluginDatabase : NSObject +{ + NSMutableDictionary *plugins; + NSMutableSet *registeredMIMETypes; + NSArray *plugInPaths; + + // Set of views with plugins attached + NSMutableSet *pluginInstanceViews; +} + ++ (WebPluginDatabase *)sharedDatabase; ++ (void)closeSharedDatabase; // avoids creating the database just to close it + +// Plug-ins are returned in this order: New plug-in (WBPL), Mach-O Netscape, CFM Netscape +- (WebBasePluginPackage *)pluginForMIMEType:(NSString *)mimeType; +- (WebBasePluginPackage *)pluginForExtension:(NSString *)extension; + +- (BOOL)isMIMETypeRegistered:(NSString *)MIMEType; + +- (NSArray *)plugins; + +- (void)refresh; + +- (void)setPlugInPaths:(NSArray *)newPaths; + +- (void)close; + +- (void)addPluginInstanceView:(NSView *)view; +- (void)removePluginInstanceView:(NSView *)view; +- (void)removePluginInstanceViewsFor:(WebFrame *)webFrame; +- (void)destroyAllPluginInstanceViews; +@end + +@interface NSObject (WebPlugInDatabase) + ++ (void)setAdditionalWebPlugInPaths:(NSArray *)path; + +@end diff --git a/Source/WebKit/mac/Plugins/WebPluginDatabase.mm b/Source/WebKit/mac/Plugins/WebPluginDatabase.mm new file mode 100644 index 0000000..e7fae1b --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebPluginDatabase.mm @@ -0,0 +1,498 @@ +/* + * 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 "WebPluginDatabase.h" + +#import "WebBaseNetscapePluginView.h" +#import "WebBasePluginPackage.h" +#import "WebDataSourcePrivate.h" +#import "WebFrame.h" +#import "WebFrameViewInternal.h" +#import "WebHTMLRepresentation.h" +#import "WebHTMLView.h" +#import "WebKitLogging.h" +#import "WebNSFileManagerExtras.h" +#import "WebNetscapePluginPackage.h" +#import "WebPluginController.h" +#import "WebPluginPackage.h" +#import "WebViewPrivate.h" +#import "WebViewInternal.h" +#import <WebKitSystemInterface.h> +#import <wtf/Assertions.h> + +using namespace WebCore; + +static void checkCandidate(WebBasePluginPackage **currentPlugin, WebBasePluginPackage **candidatePlugin); + +@interface WebPluginDatabase (Internal) ++ (NSArray *)_defaultPlugInPaths; +- (NSArray *)_plugInPaths; +- (void)_addPlugin:(WebBasePluginPackage *)plugin; +- (void)_removePlugin:(WebBasePluginPackage *)plugin; +- (NSMutableSet *)_scanForNewPlugins; +@end + +@implementation WebPluginDatabase + +static WebPluginDatabase *sharedDatabase = nil; + ++ (WebPluginDatabase *)sharedDatabase +{ + if (!sharedDatabase) { + sharedDatabase = [[WebPluginDatabase alloc] init]; + [sharedDatabase setPlugInPaths:[self _defaultPlugInPaths]]; + [sharedDatabase refresh]; + } + + return sharedDatabase; +} + ++ (void)closeSharedDatabase +{ + [sharedDatabase close]; +} + +static void checkCandidate(WebBasePluginPackage **currentPlugin, WebBasePluginPackage **candidatePlugin) +{ + if (!*currentPlugin) { + *currentPlugin = *candidatePlugin; + return; + } + + if ([*currentPlugin bundleIdentifier] == [*candidatePlugin bundleIdentifier] && [*candidatePlugin versionNumber] > [*currentPlugin versionNumber]) + *currentPlugin = *candidatePlugin; +} + +struct PluginPackageCandidates { + PluginPackageCandidates() + : webPlugin(nil) + , machoPlugin(nil) +#ifdef SUPPORT_CFM + , CFMPlugin(nil) +#endif + { + } + + void update(WebBasePluginPackage *plugin) + { + if ([plugin isKindOfClass:[WebPluginPackage class]]) { + checkCandidate(&webPlugin, &plugin); + return; + } + +#if ENABLE(NETSCAPE_PLUGIN_API) + if([plugin isKindOfClass:[WebNetscapePluginPackage class]]) { + WebExecutableType executableType = [(WebNetscapePluginPackage *)plugin executableType]; +#ifdef SUPPORT_CFM + if (executableType == WebCFMExecutableType) { + checkCandidate(&CFMPlugin, &plugin); + return; + } +#endif // SUPPORT_CFM + if (executableType == WebMachOExecutableType) { + checkCandidate(&machoPlugin, &plugin); + return; + } + } +#endif + ASSERT_NOT_REACHED(); + } + + WebBasePluginPackage *bestCandidate() + { + // Allow other plug-ins to win over QT because if the user has installed a plug-in that can handle a type + // that the QT plug-in can handle, they probably intended to override QT. + if (webPlugin && ![webPlugin isQuickTimePlugIn]) + return webPlugin; + + if (machoPlugin && ![machoPlugin isQuickTimePlugIn]) + return machoPlugin; + +#ifdef SUPPORT_CFM + if (CFMPlugin && ![CFMPlugin isQuickTimePlugIn]) + return CFMPlugin; +#endif // SUPPORT_CFM + + if (webPlugin) + return webPlugin; + if (machoPlugin) + return machoPlugin; +#ifdef SUPPORT_CFM + if (CFMPlugin) + return CFMPlugin; +#endif + return nil; + } + + WebBasePluginPackage *webPlugin; + WebBasePluginPackage *machoPlugin; +#ifdef SUPPORT_CFM + WebBasePluginPackage *CFMPlugin; +#endif +}; + +- (WebBasePluginPackage *)pluginForMIMEType:(NSString *)MIMEType +{ + PluginPackageCandidates candidates; + + MIMEType = [MIMEType lowercaseString]; + NSEnumerator *pluginEnumerator = [plugins objectEnumerator]; + + while (WebBasePluginPackage *plugin = [pluginEnumerator nextObject]) { + if ([plugin supportsMIMEType:MIMEType]) + candidates.update(plugin); + } + + return candidates.bestCandidate(); +} + +- (WebBasePluginPackage *)pluginForExtension:(NSString *)extension +{ + PluginPackageCandidates candidates; + + extension = [extension lowercaseString]; + NSEnumerator *pluginEnumerator = [plugins objectEnumerator]; + + while (WebBasePluginPackage *plugin = [pluginEnumerator nextObject]) { + if ([plugin supportsExtension:extension]) + candidates.update(plugin); + } + + WebBasePluginPackage *plugin = candidates.bestCandidate(); + + if (!plugin) { + // If no plug-in was found from the extension, attempt to map from the extension to a MIME type + // and find the a plug-in from the MIME type. This is done in case the plug-in has not fully specified + // an extension <-> MIME type mapping. + NSString *MIMEType = WKGetMIMETypeForExtension(extension); + if ([MIMEType length] > 0) + plugin = [self pluginForMIMEType:MIMEType]; + } + return plugin; +} + +- (NSArray *)plugins +{ + return [plugins allValues]; +} + +static NSArray *additionalWebPlugInPaths; + ++ (void)setAdditionalWebPlugInPaths:(NSArray *)additionalPaths +{ + if (additionalPaths == additionalWebPlugInPaths) + return; + + [additionalWebPlugInPaths release]; + additionalWebPlugInPaths = [additionalPaths copy]; + + // One might be tempted to add additionalWebPlugInPaths to the global WebPluginDatabase here. + // For backward compatibility with earlier versions of the +setAdditionalWebPlugInPaths: SPI, + // we need to save a copy of the additional paths and not cause a refresh of the plugin DB + // at this time. + // See Radars 4608487 and 4609047. +} + +- (void)setPlugInPaths:(NSArray *)newPaths +{ + if (plugInPaths == newPaths) + return; + + [plugInPaths release]; + plugInPaths = [newPaths copy]; +} + +- (void)close +{ + NSEnumerator *pluginEnumerator = [[self plugins] objectEnumerator]; + WebBasePluginPackage *plugin; + while ((plugin = [pluginEnumerator nextObject]) != nil) + [self _removePlugin:plugin]; + [plugins release]; + plugins = nil; +} + +- (id)init +{ + if (!(self = [super init])) + return nil; + + registeredMIMETypes = [[NSMutableSet alloc] init]; + pluginInstanceViews = [[NSMutableSet alloc] init]; + + return self; +} + +- (void)dealloc +{ + [plugInPaths release]; + [plugins release]; + [registeredMIMETypes release]; + [pluginInstanceViews release]; + + [super dealloc]; +} + +- (void)refresh +{ + // This method does a bit of autoreleasing, so create an autorelease pool to ensure that calling + // -refresh multiple times does not bloat the default pool. + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + // Create map from plug-in path to WebBasePluginPackage + if (!plugins) + plugins = [[NSMutableDictionary alloc] initWithCapacity:12]; + + // Find all plug-ins on disk + NSMutableSet *newPlugins = [self _scanForNewPlugins]; + + // Find plug-ins to remove from database (i.e., plug-ins that no longer exist on disk) + NSMutableSet *pluginsToRemove = [NSMutableSet set]; + NSEnumerator *pluginEnumerator = [plugins objectEnumerator]; + WebBasePluginPackage *plugin; + while ((plugin = [pluginEnumerator nextObject]) != nil) { + // Any plug-ins that were removed from disk since the last refresh should be removed from + // the database. + if (![newPlugins containsObject:plugin]) + [pluginsToRemove addObject:plugin]; + + // Remove every member of 'plugins' from 'newPlugins'. After this loop exits, 'newPlugins' + // will be the set of new plug-ins that should be added to the database. + [newPlugins removeObject:plugin]; + } + +#if !LOG_DISABLED + if ([newPlugins count] > 0) + LOG(Plugins, "New plugins:\n%@", newPlugins); + if ([pluginsToRemove count] > 0) + LOG(Plugins, "Removed plugins:\n%@", pluginsToRemove); +#endif + + // Remove plugins from database + pluginEnumerator = [pluginsToRemove objectEnumerator]; + while ((plugin = [pluginEnumerator nextObject]) != nil) + [self _removePlugin:plugin]; + + // Add new plugins to database + pluginEnumerator = [newPlugins objectEnumerator]; + while ((plugin = [pluginEnumerator nextObject]) != nil) + [self _addPlugin:plugin]; + + // Build a list of MIME types. + NSMutableSet *MIMETypes = [[NSMutableSet alloc] init]; + pluginEnumerator = [plugins objectEnumerator]; + while ((plugin = [pluginEnumerator nextObject])) { + const PluginInfo& pluginInfo = [plugin pluginInfo]; + for (size_t i = 0; i < pluginInfo.mimes.size(); ++i) + [MIMETypes addObject:pluginInfo.mimes[i].type]; + } + + // Register plug-in views and representations. + NSEnumerator *MIMEEnumerator = [MIMETypes objectEnumerator]; + NSString *MIMEType; + while ((MIMEType = [MIMEEnumerator nextObject]) != nil) { + [registeredMIMETypes addObject:MIMEType]; + + if ([WebView canShowMIMETypeAsHTML:MIMEType]) + // Don't allow plug-ins to override our core HTML types. + continue; + plugin = [self pluginForMIMEType:MIMEType]; + if ([plugin isJavaPlugIn]) + // Don't register the Java plug-in for a document view since Java files should be downloaded when not embedded. + continue; + if ([plugin isQuickTimePlugIn] && [[WebFrameView _viewTypesAllowImageTypeOmission:NO] objectForKey:MIMEType]) + // Don't allow the QT plug-in to override any types because it claims many that we can handle ourselves. + continue; + + if (self == sharedDatabase) + [WebView _registerPluginMIMEType:MIMEType]; + } + [MIMETypes release]; + + [pool drain]; +} + +- (BOOL)isMIMETypeRegistered:(NSString *)MIMEType +{ + return [registeredMIMETypes containsObject:MIMEType]; +} + +- (void)addPluginInstanceView:(NSView *)view +{ + [pluginInstanceViews addObject:view]; +} + +- (void)removePluginInstanceView:(NSView *)view +{ + [pluginInstanceViews removeObject:view]; +} + +- (void)removePluginInstanceViewsFor:(WebFrame*)webFrame +{ + // This handles handles the case where a frame or view is being destroyed and the plugin needs to be removed from the list first + + if( [pluginInstanceViews count] == 0 ) + return; + + NSView <WebDocumentView> *documentView = [[webFrame frameView] documentView]; + if ([documentView isKindOfClass:[WebHTMLView class]]) { + NSArray *subviews = [documentView subviews]; + unsigned int subviewCount = [subviews count]; + unsigned int subviewIndex; + + for (subviewIndex = 0; subviewIndex < subviewCount; subviewIndex++) { + NSView *subview = [subviews objectAtIndex:subviewIndex]; +#if ENABLE(NETSCAPE_PLUGIN_API) + if ([subview isKindOfClass:[WebBaseNetscapePluginView class]] || [WebPluginController isPlugInView:subview]) +#else + if ([WebPluginController isPlugInView:subview]) +#endif + [pluginInstanceViews removeObject:subview]; + } + } +} + +- (void)destroyAllPluginInstanceViews +{ + NSView *view; + NSArray *pli = [pluginInstanceViews allObjects]; + NSEnumerator *enumerator = [pli objectEnumerator]; + while ((view = [enumerator nextObject]) != nil) { +#if ENABLE(NETSCAPE_PLUGIN_API) + if ([view isKindOfClass:[WebBaseNetscapePluginView class]]) { + ASSERT([view respondsToSelector:@selector(stop)]); + [view performSelector:@selector(stop)]; + } else +#endif + if ([WebPluginController isPlugInView:view]) { + ASSERT([[view superview] isKindOfClass:[WebHTMLView class]]); + ASSERT([[view superview] respondsToSelector:@selector(_destroyAllWebPlugins)]); + // this will actually destroy all plugin instances for a webHTMLView and remove them from this list + [[view superview] performSelector:@selector(_destroyAllWebPlugins)]; + } + } +} + +@end + +@implementation WebPluginDatabase (Internal) + ++ (NSArray *)_defaultPlugInPaths +{ + // Plug-ins are found in order of precedence. + // If there are duplicates, the first found plug-in is used. + // For example, if there is a QuickTime.plugin in the users's home directory + // that is used instead of the /Library/Internet Plug-ins version. + // The purpose is to allow non-admin users to update their plug-ins. + return [NSArray arrayWithObjects: + [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Internet Plug-Ins"], + @"/Library/Internet Plug-Ins", + [[NSBundle mainBundle] builtInPlugInsPath], + nil]; +} + +- (NSArray *)_plugInPaths +{ + if (self == sharedDatabase && additionalWebPlugInPaths) { + // Add additionalWebPlugInPaths to the global WebPluginDatabase. We do this here for + // backward compatibility with earlier versions of the +setAdditionalWebPlugInPaths: SPI, + // which simply saved a copy of the additional paths and did not cause the plugin DB to + // refresh. See Radars 4608487 and 4609047. + NSMutableArray *modifiedPlugInPaths = [[plugInPaths mutableCopy] autorelease]; + [modifiedPlugInPaths addObjectsFromArray:additionalWebPlugInPaths]; + return modifiedPlugInPaths; + } else + return plugInPaths; +} + +- (void)_addPlugin:(WebBasePluginPackage *)plugin +{ + ASSERT(plugin); + NSString *pluginPath = [plugin path]; + ASSERT(pluginPath); + [plugins setObject:plugin forKey:pluginPath]; + [plugin wasAddedToPluginDatabase:self]; +} + +- (void)_removePlugin:(WebBasePluginPackage *)plugin +{ + ASSERT(plugin); + + // Unregister plug-in's MIME type registrations + const PluginInfo& pluginInfo = [plugin pluginInfo]; + for (size_t i = 0; i < pluginInfo.mimes.size(); ++i) { + NSString *MIMEType = pluginInfo.mimes[i].type; + + if ([registeredMIMETypes containsObject:MIMEType]) { + if (self == sharedDatabase) + [WebView _unregisterPluginMIMEType:MIMEType]; + [registeredMIMETypes removeObject:MIMEType]; + } + } + + // Remove plug-in from database + NSString *pluginPath = [plugin path]; + ASSERT(pluginPath); + [plugin retain]; + [plugins removeObjectForKey:pluginPath]; + [plugin wasRemovedFromPluginDatabase:self]; + [plugin release]; +} + +- (NSMutableSet *)_scanForNewPlugins +{ + NSMutableSet *newPlugins = [NSMutableSet set]; + NSEnumerator *directoryEnumerator = [[self _plugInPaths] objectEnumerator]; + NSMutableSet *uniqueFilenames = [[NSMutableSet alloc] init]; + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSString *pluginDirectory; + while ((pluginDirectory = [directoryEnumerator nextObject]) != nil) { + // Get contents of each plug-in directory + NSEnumerator *filenameEnumerator = [[fileManager contentsOfDirectoryAtPath:pluginDirectory error:NULL] objectEnumerator]; + NSString *filename; + while ((filename = [filenameEnumerator nextObject]) != nil) { + // Unique plug-ins by filename + if ([uniqueFilenames containsObject:filename]) + continue; + [uniqueFilenames addObject:filename]; + + // Create a plug-in package for this path + NSString *pluginPath = [pluginDirectory stringByAppendingPathComponent:filename]; + WebBasePluginPackage *pluginPackage = [plugins objectForKey:pluginPath]; + if (!pluginPackage) + pluginPackage = [WebBasePluginPackage pluginWithPath:pluginPath]; + if (pluginPackage) + [newPlugins addObject:pluginPackage]; + } + } + [uniqueFilenames release]; + + return newPlugins; +} + +@end diff --git a/Source/WebKit/mac/Plugins/WebPluginPackage.h b/Source/WebKit/mac/Plugins/WebPluginPackage.h new file mode 100644 index 0000000..f8c8146 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebPluginPackage.h @@ -0,0 +1,41 @@ +/* + * 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/WebBasePluginPackage.h> + +@protocol WebPluginViewFactory; + +@interface WebPluginPackage : WebBasePluginPackage { + NSBundle *nsBundle; +} + +- (Class)viewFactory; + +@end diff --git a/Source/WebKit/mac/Plugins/WebPluginPackage.mm b/Source/WebKit/mac/Plugins/WebPluginPackage.mm new file mode 100644 index 0000000..a608068 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebPluginPackage.mm @@ -0,0 +1,125 @@ +/* + * 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/WebPluginPackage.h> + +#import <WebKit/WebKitLogging.h> +#import <WebKit/WebKitNSStringExtras.h> + +NSString *WebPlugInBaseURLKey = @"WebPlugInBaseURLKey"; +NSString *WebPlugInAttributesKey = @"WebPlugInAttributesKey"; +NSString *WebPlugInContainerKey = @"WebPlugInContainerKey"; +NSString *WebPlugInModeKey = @"WebPlugInModeKey"; +NSString *WebPlugInShouldLoadMainResourceKey = @"WebPlugInShouldLoadMainResourceKey"; +NSString *WebPlugInContainingElementKey = @"WebPlugInContainingElementKey"; + +@implementation WebPluginPackage + +- (id)initWithPath:(NSString *)pluginPath +{ + if (!(self = [super initWithPath:pluginPath])) + return nil; + + nsBundle = [[NSBundle alloc] initWithPath:path]; + + if (!nsBundle) { + [self release]; + return nil; + } + + if (![[pluginPath pathExtension] _webkit_isCaseInsensitiveEqualToString:@"webplugin"]) { + UInt32 type = 0; + CFBundleGetPackageInfo(cfBundle.get(), &type, NULL); + if (type != FOUR_CHAR_CODE('WBPL')) { + [self release]; + return nil; + } + } + + NSFileHandle *executableFile = [NSFileHandle fileHandleForReadingAtPath:[nsBundle executablePath]]; + NSData *data = [executableFile readDataOfLength:512]; + [executableFile closeFile]; + if (![self isNativeLibraryData:data]) { + [self release]; + return nil; + } + + if (![self getPluginInfoFromPLists]) { + [self release]; + return nil; + } + + return self; +} + +- (void)dealloc +{ + [nsBundle release]; + + [super dealloc]; +} + +- (Class)viewFactory +{ + return [nsBundle principalClass]; +} + +- (BOOL)load +{ +#if !LOG_DISABLED + CFAbsoluteTime start = CFAbsoluteTimeGetCurrent(); +#endif + + // Load the bundle + if (![nsBundle isLoaded]) { + if (![nsBundle load]) + return NO; + } + +#if !LOG_DISABLED + CFAbsoluteTime duration = CFAbsoluteTimeGetCurrent() - start; + LOG(Plugins, "principalClass took %f seconds for: %@", duration, (NSString *)[self pluginInfo].name); +#endif + return [super load]; +} + +@end + +@implementation NSObject (WebScripting) + ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector +{ + return YES; +} + ++ (BOOL)isKeyExcludedFromWebScript:(const char *)name +{ + return YES; +} + +@end diff --git a/Source/WebKit/mac/Plugins/WebPluginPrivate.h b/Source/WebKit/mac/Plugins/WebPluginPrivate.h new file mode 100644 index 0000000..56e4a3a --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebPluginPrivate.h @@ -0,0 +1,31 @@ +/* + * 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. + * 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. + */ + +@interface NSObject (WebPlugInPrivate) + +@end diff --git a/Source/WebKit/mac/Plugins/WebPluginRequest.h b/Source/WebKit/mac/Plugins/WebPluginRequest.h new file mode 100644 index 0000000..5336dcb --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebPluginRequest.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 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. + */ + +#if ENABLE(NETSCAPE_PLUGIN_API) + +@interface WebPluginRequest : NSObject +{ + NSURLRequest *_request; + NSString *_frameName; + void *_notifyData; + BOOL _didStartFromUserGesture; + BOOL _sendNotification; +} + +- (id)initWithRequest:(NSURLRequest *)request frameName:(NSString *)frameName notifyData:(void *)notifyData sendNotification:(BOOL)sendNotification didStartFromUserGesture:(BOOL)currentEventIsUserGesture; + +- (NSURLRequest *)request; +- (NSString *)frameName; +- (void *)notifyData; +- (BOOL)isCurrentEventUserGesture; +- (BOOL)sendNotification; + +@end + +#endif // ENABLE(NETSCAPE_PLUGIN_API) diff --git a/Source/WebKit/mac/Plugins/WebPluginRequest.m b/Source/WebKit/mac/Plugins/WebPluginRequest.m new file mode 100644 index 0000000..df36d40 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebPluginRequest.m @@ -0,0 +1,80 @@ +/* + * Copyright (C) 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. + */ + +#if ENABLE(NETSCAPE_PLUGIN_API) + +#import "WebPluginRequest.h" + +@implementation WebPluginRequest + +- (id)initWithRequest:(NSURLRequest *)request frameName:(NSString *)frameName notifyData:(void *)notifyData sendNotification:(BOOL)sendNotification didStartFromUserGesture:(BOOL)currentEventIsUserGesture +{ + [super init]; + _didStartFromUserGesture = currentEventIsUserGesture; + _request = [request retain]; + _frameName = [frameName retain]; + _notifyData = notifyData; + _sendNotification = sendNotification; + return self; +} + +- (void)dealloc +{ + [_request release]; + [_frameName release]; + [super dealloc]; +} + +- (NSURLRequest *)request +{ + return _request; +} + +- (NSString *)frameName +{ + return _frameName; +} + +- (BOOL)isCurrentEventUserGesture +{ + return _didStartFromUserGesture; +} + +- (BOOL)sendNotification +{ + return _sendNotification; +} + +- (void *)notifyData +{ + return _notifyData; +} + +@end + +#endif // ENABLE(NETSCAPE_PLUGIN_API) diff --git a/Source/WebKit/mac/Plugins/WebPluginViewFactory.h b/Source/WebKit/mac/Plugins/WebPluginViewFactory.h new file mode 100644 index 0000000..a215906 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebPluginViewFactory.h @@ -0,0 +1,86 @@ +/* + * 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. + * 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> + +/*! + @constant WebPlugInBaseURLKey REQUIRED. The base URL of the document containing + the plug-in's view. +*/ +extern NSString *WebPlugInBaseURLKey; + +/*! + @constant WebPlugInAttributesKey REQUIRED. The dictionary containing the names + and values of all attributes of the HTML element associated with the plug-in AND + the names and values of all parameters to be passed to the plug-in (e.g. PARAM + elements within an APPLET element). In the case of a conflict between names, + the attributes of an element take precedence over any PARAMs. All of the keys + and values in this NSDictionary must be NSStrings. +*/ +extern NSString *WebPlugInAttributesKey; + +/*! + @constant WebPlugInContainer OPTIONAL. An object that conforms to the + WebPlugInContainer informal protocol. This object is used for + callbacks from the plug-in to the app. if this argument is nil, no callbacks will + occur. +*/ +extern NSString *WebPlugInContainerKey; + +/*! + @constant WebPlugInContainingElementKey The DOMElement that was used to specify + the plug-in. May be nil. +*/ +extern NSString *WebPlugInContainingElementKey; + +/*! + @constant WebPlugInShouldLoadMainResourceKey REQUIRED. NSNumber (BOOL) indicating whether the plug-in should load its + own main resource (the "src" URL, in most cases). If YES, the plug-in should load its own main resource. If NO, the + plug-in should use the data provided by WebKit. See -webPlugInMainResourceReceivedData: in WebPluginPrivate.h. + For compatibility with older versions of WebKit, the plug-in should assume that the value for + WebPlugInShouldLoadMainResourceKey is NO if it is absent from the arguments dictionary. + */ +extern NSString *WebPlugInShouldLoadMainResourceKey AVAILABLE_IN_WEBKIT_VERSION_4_0; + +/*! + @protocol WebPlugInViewFactory + @discussion WebPlugInViewFactory are used to create the NSView for a plug-in. + The principal class of the plug-in bundle must implement this protocol. +*/ + +@protocol WebPlugInViewFactory <NSObject> + +/*! + @method plugInViewWithArguments: + @param arguments The arguments dictionary with the mentioned keys and objects. This method is required to implement. + @result Returns an NSView object that conforms to the WebPlugIn informal protocol. +*/ ++ (NSView *)plugInViewWithArguments:(NSDictionary *)arguments; + +@end diff --git a/Source/WebKit/mac/Plugins/WebPluginViewFactoryPrivate.h b/Source/WebKit/mac/Plugins/WebPluginViewFactoryPrivate.h new file mode 100644 index 0000000..51d4283 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebPluginViewFactoryPrivate.h @@ -0,0 +1,39 @@ +/* + * 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/WebPluginViewFactory.h> + +typedef enum { + WebPlugInModeEmbed = 0, + WebPlugInModeFull = 1 +} WebPlugInMode; + +/*! + @constant WebPlugInModeKey REQUIRED. Number with one of the values from the WebPlugInMode enum. +*/ +extern NSString *WebPlugInModeKey; diff --git a/Source/WebKit/mac/Plugins/WebPluginsPrivate.h b/Source/WebKit/mac/Plugins/WebPluginsPrivate.h new file mode 100644 index 0000000..fc0067d --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebPluginsPrivate.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. + */ + +// WebPluginWillPresentNativeUserInterfaceNotification is sent by plugins to notify the WebKit client +// application that the plugin will present a dialog or some other "native" user interface to the user. +// This is currently only needed by Dashboard, which must hide itself when a plugin presents a dialog +// box (4318632). +extern NSString *WebPluginWillPresentNativeUserInterfaceNotification; diff --git a/Source/WebKit/mac/Plugins/WebPluginsPrivate.m b/Source/WebKit/mac/Plugins/WebPluginsPrivate.m new file mode 100644 index 0000000..79896b6 --- /dev/null +++ b/Source/WebKit/mac/Plugins/WebPluginsPrivate.m @@ -0,0 +1,31 @@ +/* + * 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. + */ + +#include "WebPluginsPrivate.h" + +NSString *WebPluginWillPresentNativeUserInterfaceNotification = @"WebPluginWillPresentNativeUserInterface"; diff --git a/Source/WebKit/mac/Plugins/npapi.mm b/Source/WebKit/mac/Plugins/npapi.mm new file mode 100644 index 0000000..8b1cfd6 --- /dev/null +++ b/Source/WebKit/mac/Plugins/npapi.mm @@ -0,0 +1,235 @@ +/* + * 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. + */ + +#if ENABLE(NETSCAPE_PLUGIN_API) +#import <WebKit/npapi.h> + +#import "WebNetscapePluginView.h" +#import "WebKitLogging.h" +#import <WebCore/PluginMainThreadScheduler.h> + +using namespace WebCore; + +WebNetscapePluginView *pluginViewForInstance(NPP instance); + +// general plug-in to browser functions + +void* NPN_MemAlloc(uint32_t size) +{ + return malloc(size); +} + +void NPN_MemFree(void* ptr) +{ + free(ptr); +} + +uint32_t NPN_MemFlush(uint32_t size) +{ + LOG(Plugins, "NPN_MemFlush"); + return size; +} + +void NPN_ReloadPlugins(NPBool reloadPages) +{ + LOG(Plugins, "NPN_ReloadPlugins"); +} + +NPError NPN_RequestRead(NPStream* stream, NPByteRange* rangeList) +{ + LOG(Plugins, "NPN_RequestRead"); + return NPERR_GENERIC_ERROR; +} + +// instance-specific functions +// The plugin view is always the ndata of the instance. Sometimes, plug-ins will call an instance-specific function +// with a NULL instance. To workaround this, call the last plug-in view that made a call to a plug-in. +// Currently, the current plug-in view is only set before NPP_New in [WebNetscapePluginView start]. +// This specifically works around Flash and Shockwave. When we call NPP_New, they call NPN_UserAgent with a NULL instance. +WebNetscapePluginView *pluginViewForInstance(NPP instance) +{ + if (instance && instance->ndata) + return (WebNetscapePluginView *)instance->ndata; + else + return [WebNetscapePluginView currentPluginView]; +} + +NPError NPN_GetURLNotify(NPP instance, const char* URL, const char* target, void* notifyData) +{ + return [pluginViewForInstance(instance) getURLNotify:URL target:target notifyData:notifyData]; +} + +NPError NPN_GetURL(NPP instance, const char* URL, const char* target) +{ + return [pluginViewForInstance(instance) getURL:URL target:target]; +} + +NPError NPN_PostURLNotify(NPP instance, const char* URL, const char* target, uint32_t len, const char* buf, NPBool file, void* notifyData) +{ + return [pluginViewForInstance(instance) postURLNotify:URL target:target len:len buf:buf file:file notifyData:notifyData]; +} + +NPError NPN_PostURL(NPP instance, const char* URL, const char* target, uint32_t len, const char* buf, NPBool file) +{ + return [pluginViewForInstance(instance) postURL:URL target:target len:len buf:buf file:file]; +} + +NPError NPN_NewStream(NPP instance, NPMIMEType type, const char* target, NPStream** stream) +{ + return [pluginViewForInstance(instance) newStream:type target:target stream:stream]; +} + +int32_t NPN_Write(NPP instance, NPStream* stream, int32_t len, void* buffer) +{ + return [pluginViewForInstance(instance) write:stream len:len buffer:buffer]; +} + +NPError NPN_DestroyStream(NPP instance, NPStream* stream, NPReason reason) +{ + return [pluginViewForInstance(instance) destroyStream:stream reason:reason]; +} + +const char* NPN_UserAgent(NPP instance) +{ + return [pluginViewForInstance(instance) userAgent]; +} + +void NPN_Status(NPP instance, const char* message) +{ + [pluginViewForInstance(instance) status:message]; +} + +void NPN_InvalidateRect(NPP instance, NPRect *invalidRect) +{ + [pluginViewForInstance(instance) invalidateRect:invalidRect]; +} + +void NPN_InvalidateRegion(NPP instance, NPRegion invalidRegion) +{ + [pluginViewForInstance(instance) invalidateRegion:invalidRegion]; +} + +void NPN_ForceRedraw(NPP instance) +{ + [pluginViewForInstance(instance) forceRedraw]; +} + +NPError NPN_GetValue(NPP instance, NPNVariable variable, void *value) +{ + return [pluginViewForInstance(instance) getVariable:variable value:value]; +} + +NPError NPN_SetValue(NPP instance, NPPVariable variable, void *value) +{ + return [pluginViewForInstance(instance) setVariable:variable value:value]; +} + +// Unsupported functions + +void* NPN_GetJavaEnv(void) +{ + LOG(Plugins, "NPN_GetJavaEnv"); + return NULL; +} + +void* NPN_GetJavaPeer(NPP instance) +{ + LOG(Plugins, "NPN_GetJavaPeer"); + return NULL; +} + +void NPN_PushPopupsEnabledState(NPP instance, NPBool enabled) +{ +} + +void NPN_PopPopupsEnabledState(NPP instance) +{ +} + +void NPN_PluginThreadAsyncCall(NPP instance, void (*func) (void *), void *userData) +{ + PluginMainThreadScheduler::scheduler().scheduleCall(instance, func, userData); +} + +uint32_t NPN_ScheduleTimer(NPP instance, uint32_t interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32_t timerID)) +{ + return [pluginViewForInstance(instance) scheduleTimerWithInterval:interval repeat:repeat timerFunc:timerFunc]; +} + +void NPN_UnscheduleTimer(NPP instance, uint32_t timerID) +{ + [pluginViewForInstance(instance) unscheduleTimer:timerID]; +} + +NPError NPN_PopUpContextMenu(NPP instance, NPMenu *menu) +{ + return [pluginViewForInstance(instance) popUpContextMenu:menu]; +} + +NPError NPN_GetValueForURL(NPP instance, NPNURLVariable variable, const char* url, char** value, uint32_t* len) +{ + return [pluginViewForInstance(instance) getVariable:variable forURL:url value:value length:len]; +} + +NPError NPN_SetValueForURL(NPP instance, NPNURLVariable variable, const char* url, const char* value, uint32_t len) +{ + return [pluginViewForInstance(instance) setVariable:variable forURL:url value:value length:len]; +} + +NPError NPN_GetAuthenticationInfo(NPP instance, const char* protocol, const char* host, int32_t port, const char* scheme, const char *realm, char** username, uint32_t* ulen, char** password, uint32_t* plen) +{ + return [pluginViewForInstance(instance) getAuthenticationInfoWithProtocol:protocol + host:host + port:port + scheme:scheme + realm:realm + username:username usernameLength:ulen + password:password passwordLength:plen]; +} + +NPBool NPN_ConvertPoint(NPP instance, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace) +{ + return [pluginViewForInstance(instance) convertFromX:sourceX andY:sourceY space:sourceSpace toX:destX andY:destY space:destSpace]; +} + +uint32_t WKN_CheckIfAllowedToLoadURL(NPP instance, const char* url, const char* frame, void (*callbackFunc)(NPP npp, uint32_t, NPBool, void*), void* context) +{ + return [pluginViewForInstance(instance) checkIfAllowedToLoadURL:url frame:frame callbackFunc:callbackFunc context:context]; +} + +void WKN_CancelCheckIfAllowedToLoadURL(NPP instance, uint32_t checkID) +{ + [pluginViewForInstance(instance) cancelCheckIfAllowedToLoadURL:checkID]; +} + +char* WKN_ResolveURL(NPP instance, const char* url, const char* target) +{ + return [pluginViewForInstance(instance) resolveURL:url forTarget:target]; +} + +#endif |
