diff options
Diffstat (limited to 'WebKit/mac/Plugins')
40 files changed, 9018 insertions, 0 deletions
diff --git a/WebKit/mac/Plugins/WebBaseNetscapePluginStream.h b/WebKit/mac/Plugins/WebBaseNetscapePluginStream.h new file mode 100644 index 0000000..c2e8a21 --- /dev/null +++ b/WebKit/mac/Plugins/WebBaseNetscapePluginStream.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 <WebKit/WebPlugInStreamLoaderDelegate.h> +#import <wtf/PassRefPtr.h> +#import <wtf/RefCounted.h> +#import <wtf/RefPtr.h> +#import <wtf/RetainPtr.h> + +#import "WebBaseNetscapePluginView.h" + +namespace WebCore { + class FrameLoader; + class NetscapePlugInStreamLoader; +} + +@class WebBaseNetscapePluginView; +@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(NSURL *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, NSString *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; + RetainPtr<NSURL> m_requestURL; + RetainPtr<NSURL> m_responseURL; + RetainPtr<NSString> m_mimeType; + + NPP m_plugin; + uint16 m_transferMode; + int32 m_offset; + NPStream m_stream; + RetainPtr<NSString> m_path; + int m_fileDescriptor; + BOOL m_sendNotification; + void *m_notifyData; + char *m_headers; + RetainPtr<WebBaseNetscapePluginView> 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/WebKit/mac/Plugins/WebBaseNetscapePluginStream.mm b/WebKit/mac/Plugins/WebBaseNetscapePluginStream.mm new file mode 100644 index 0000000..246a3ca --- /dev/null +++ b/WebKit/mac/Plugins/WebBaseNetscapePluginStream.mm @@ -0,0 +1,619 @@ +/* + * 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 "WebBaseNetscapePluginStream.h" + +#import "WebBaseNetscapePluginView.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/WebCoreObjCExtras.h> +#import <WebKitSystemInterface.h> +#import <wtf/HashMap.h> + +using namespace WebCore; + +#define WEB_REASON_NONE -1 + +static NSString *CarbonPathFromPOSIXPath(NSString *posixPath); + +typedef HashMap<NPStream*, NPP> StreamMap; +static StreamMap& streams() +{ + static 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() : m_requestURL.get() + pluginPageURL:nil + pluginName:[[m_pluginView.get() pluginPackage] name] + MIMEType:m_mimeType.get()] 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() : m_requestURL.get()]; + + 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)); + + WebBaseNetscapePluginView *view = (WebBaseNetscapePluginView *)plugin->ndata; + + // This check has already been done by the plug-in view. + ASSERT(FrameLoader::canLoad([request URL], String(), core([view webFrame])->document())); + + ASSERT([request URL]); + ASSERT(plugin); + + setPlugin(plugin); + + streams().add(&m_stream, plugin); + + if (core([view webFrame])->loader()->shouldHideReferrer([request URL], core([view webFrame])->loader()->outgoingReferrer())) + [m_request.get() _web_setHTTPReferrer:nil]; + + m_loader = NetscapePlugInStreamLoader::create(core([view webFrame]), this); + m_loader->setShouldBufferData(false); +} + +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<WebBaseNetscapePluginView *>(m_plugin->ndata); + + WebNetscapePluginPackage *pluginPackage = [m_pluginView.get() pluginPackage]; + + m_pluginFuncs = [pluginPackage pluginFuncs]; + } else { + WebBaseNetscapePluginView *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, NSString *mimeType, NSData *headers) +{ + ASSERT(!m_isTerminated); + + m_responseURL = url; + m_mimeType = mimeType; + + free((void *)m_stream.url); + m_stream.url = strdup([m_responseURL.get() _web_URLCString]); + + m_stream.ndata = this; + m_stream.end = expectedContentLength > 0 ? (uint32)expectedContentLength : 0; + m_stream.lastmodified = (uint32)[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 + + [m_pluginView.get() willCallPlugInFunction]; + NPError npErr = m_pluginFuncs->newstream(m_plugin, (char *)[m_mimeType.get() UTF8String], &m_stream, NO, &m_transferMode); + [m_pluginView.get() didCallPlugInFunction]; + LOG(Plugins, "NPP_NewStream URL=%@ MIME=%@ error=%d", m_responseURL.get(), m_mimeType.get(), 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); + + m_loader->documentLoader()->addPlugInStreamLoader(m_loader.get()); + m_loader->load(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), [r 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; + [m_pluginView.get() willCallPlugInFunction]; + { + JSC::JSLock::DropAllLocks dropAllLocks(false); + error = m_pluginFuncs->getvalue(m_plugin, NPPVpluginWantsAllNetworkStreams, &value); + } + [m_pluginView.get() didCallPlugInFunction]; + 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); + [m_pluginView.get() willCallPlugInFunction]; + m_pluginFuncs->asfile(m_plugin, &m_stream, [carbonPath fileSystemRepresentation]); + [m_pluginView.get() didCallPlugInFunction]; + 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) { + [m_pluginView.get() willCallPlugInFunction]; +#if !LOG_DISABLED + NPError npErr = +#endif + m_pluginFuncs->destroystream(m_plugin, &m_stream, m_reason); + [m_pluginView.get() didCallPlugInFunction]; + 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. + [m_pluginView.get() willCallPlugInFunction]; + m_pluginFuncs->urlnotify(m_plugin, [m_requestURL.get() _web_URLCString], m_reason, m_notifyData); + [m_pluginView.get() didCallPlugInFunction]; + LOG(Plugins, "NPP_URLNotify requestURL=%@ reason=%d", m_requestURL.get(), 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; + } + 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 totalBytes = [m_deliveryData.get() length]; + int32 totalBytesDelivered = 0; + + while (totalBytesDelivered < totalBytes) { + [m_pluginView.get() willCallPlugInFunction]; + int32 deliveryBytes = m_pluginFuncs->writeready(m_plugin, &m_stream); + [m_pluginView.get() didCallPlugInFunction]; + 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)]; + [m_pluginView.get() willCallPlugInFunction]; + deliveryBytes = m_pluginFuncs->write(m_plugin, &m_stream, m_offset, [subdata length], (void *)[subdata bytes]); + [m_pluginView.get() didCallPlugInFunction]; + if (deliveryBytes < 0) { + // Netscape documentation says that a negative result from NPP_Write means cancel the load. + cancelLoadAndDestroyStreamWithError(pluginCancelledConnectionError()); + return; + } + deliveryBytes = MIN((unsigned)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.get() release]; + m_deliveryData = newDeliveryData; + [newDeliveryData release]; + } 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/WebKit/mac/Plugins/WebBaseNetscapePluginView.h b/WebKit/mac/Plugins/WebBaseNetscapePluginView.h new file mode 100644 index 0000000..909196b --- /dev/null +++ b/WebKit/mac/Plugins/WebBaseNetscapePluginView.h @@ -0,0 +1,225 @@ +/* + * 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 <WebKit/npfunctions.h> +#import <WebKit/npapi.h> +#import <WebKit/WebBasePluginPackage.h> +#import <wtf/HashMap.h> +#import <wtf/HashSet.h> + +@class DOMElement; +@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; + +typedef struct _NPPluginTextInputFuncs NPPluginTextInputFuncs; + +// 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 WebBaseNetscapePluginView WebNetscapePluginDocumentView + +@interface WebBaseNetscapePluginView : NSView <WebPluginManualLoader, NSTextInput> +{ + WebNetscapePluginPackage *pluginPackage; + + NSURL *sourceURL; + WebFrame *_webFrame; + + BOOL _loadManually; + RefPtr<WebNetscapePluginStream> _manualStream; +#ifndef BUILDING_ON_TIGER + CALayer *_layer; +#endif + unsigned _dataLengthReceived; + NSError *_error; + + int mode; + + 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 + + WebNetscapePluginEventHandler *eventHandler; + + BOOL isStarted; + BOOL inSetWindow; + BOOL hasFocus; + BOOL isTransparent; + BOOL isCompletelyObscured; + BOOL shouldStopSoon; + + BOOL shouldFireTimers; + uint32 currentTimerID; + HashMap<uint32, PluginTimer*>* timers; + + unsigned pluginFunctionCallDepth; + + DOMElement *element; + + int32 specifiedHeight; + int32 specifiedWidth; + + NSString *MIMEType; + NSURL *baseURL; + NSTrackingRectTag trackingTag; + + HashSet<RefPtr<WebNetscapePluginStream> > streams; + NSMutableDictionary *pendingFrameLoads; + + NPPluginTextInputFuncs *textInputFuncs; + + NPP_NewProcPtr NPP_New; + NPP_DestroyProcPtr NPP_Destroy; + NPP_SetWindowProcPtr NPP_SetWindow; + NPP_NewStreamProcPtr NPP_NewStream; + NPP_DestroyStreamProcPtr NPP_DestroyStream; + NPP_StreamAsFileProcPtr NPP_StreamAsFile; + NPP_WriteReadyProcPtr NPP_WriteReady; + NPP_WriteProcPtr NPP_Write; + NPP_PrintProcPtr NPP_Print; + NPP_HandleEventProcPtr NPP_HandleEvent; + NPP_URLNotifyProcPtr NPP_URLNotify; + NPP_GetValueProcPtr NPP_GetValue; + NPP_SetValueProcPtr NPP_SetValue; +} + ++ (WebBaseNetscapePluginView *)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 + DOMElement:(DOMElement *)anElement; + + +- (BOOL)start; +- (BOOL)isStarted; +- (void)stop; +- (void)stopTimers; +- (void)restartTimers; + +- (WebFrame *)webFrame; +- (WebDataSource *)dataSource; +- (WebView *)webView; +- (NSWindow *)currentWindow; + +- (NPP)plugin; + +- (WebNetscapePluginPackage *)pluginPackage; +- (void)setPluginPackage:(WebNetscapePluginPackage *)thePluginPackage; +- (void)setMIMEType:(NSString *)theMIMEType; +- (void)setBaseURL:(NSURL *)theBaseURL; +- (void)setAttributeKeys:(NSArray *)keys andValues:(NSArray *)values; +- (void)setMode:(int)theMode; +- (void)viewWillMoveToHostWindow:(NSWindow *)hostWindow; +- (void)viewDidMoveToHostWindow; +- (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; + +@end + +@interface WebBaseNetscapePluginView (WebInternal) +- (BOOL)sendEvent:(void*)event isDrawRect:(BOOL)eventIsDrawRect; +- (NPEventModel)eventModel; + +- (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)scheduleTimerWithInterval:(uint32)interval repeat:(NPBool)repeat timerFunc:(void (*)(NPP npp, uint32 timerID))timerFunc; +- (void)unscheduleTimer:(uint32)timerID; +- (NPError)popUpContextMenu:(NPMenu *)menu; + +@end + +#endif + diff --git a/WebKit/mac/Plugins/WebBaseNetscapePluginView.mm b/WebKit/mac/Plugins/WebBaseNetscapePluginView.mm new file mode 100644 index 0000000..2097673 --- /dev/null +++ b/WebKit/mac/Plugins/WebBaseNetscapePluginView.mm @@ -0,0 +1,2948 @@ +/* + * 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 "WebBaseNetscapePluginView.h" + +#import "WebDataSourceInternal.h" +#import "WebDefaultUIDelegate.h" +#import "WebFrameInternal.h" +#import "WebFrameView.h" +#import "WebGraphicsExtras.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 "WebNetscapePluginPackage.h" +#import "WebBaseNetscapePluginStream.h" +#import "WebNetscapePluginEventHandler.h" +#import "WebNullPluginView.h" +#import "WebPreferences.h" +#import "WebViewInternal.h" +#import "WebUIDelegatePrivate.h" +#import <Carbon/Carbon.h> +#import <runtime/JSLock.h> +#import <WebCore/npruntime_impl.h> +#import <WebCore/Document.h> +#import <WebCore/DocumentLoader.h> +#import <WebCore/Element.h> +#import <WebCore/Frame.h> +#import <WebCore/FrameLoader.h> +#import <WebCore/FrameTree.h> +#import <WebCore/Page.h> +#import <WebCore/PluginMainThreadScheduler.h> +#import <WebCore/ScriptController.h> +#import <WebCore/SoftLinking.h> +#import <WebCore/WebCoreObjCExtras.h> +#import <WebKit/nptextinput.h> +#import <WebKit/DOMPrivate.h> +#import <WebKit/WebUIDelegate.h> +#import <wtf/Assertions.h> +#import <objc/objc-runtime.h> + +using namespace WebCore; + +#define LoginWindowDidSwitchFromUserNotification @"WebLoginWindowDidSwitchFromUserNotification" +#define LoginWindowDidSwitchToUserNotification @"WebLoginWindowDidSwitchToUserNotification" + +static inline bool isDrawingModelQuickDraw(NPDrawingModel drawingModel) +{ +#ifndef NP_NO_QUICKDRAW + return drawingModel == NPDrawingModelQuickDraw; +#else + return false; +#endif +}; + +@interface WebBaseNetscapePluginView (Internal) +- (void)_viewHasMoved; +- (NPError)_createPlugin; +- (void)_destroyPlugin; +- (NSBitmapImageRep *)_printedPluginBitmap; +- (void)_redeliverStream; +@end + +static WebBaseNetscapePluginView *currentPluginView = nil; + +typedef struct OpaquePortState* PortState; + +static const double ThrottledTimerInterval = 0.25; + +class PluginTimer : public TimerBase { +public: + typedef void (*TimerFunc)(NPP npp, uint32 timerID); + + PluginTimer(NPP npp, uint32 timerID, uint32 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 = throttle ? ThrottledTimerInterval : m_interval / 1000.0; + 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 m_timerID; + uint32 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 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 + +@interface NSData (WebPluginDataExtras) +- (BOOL)_web_startsWithBlankLine; +- (NSInteger)_web_locationAfterFirstBlankLine; +@end + +@interface WebBaseNetscapePluginView (ForwardDeclarations) +- (void)setWindowIfNecessary; +- (NPError)loadRequest:(NSMutableURLRequest *)request inTarget:(const char *)cTarget withNotifyData:(void *)notifyData sendNotification:(BOOL)sendNotification; +@end + +@implementation WebBaseNetscapePluginView + ++ (void)initialize +{ +#ifndef BUILDING_ON_TIGER + WebCoreObjCFinalizeOnMainThread(self); +#endif + WKSendUserChangeNotifications(); +} + +#pragma mark EVENTS + +- (BOOL)superviewsHaveSuperviews +{ + NSView *contentView = [[self window] contentView]; + NSView *view; + for (view = self; view != nil; view = [view superview]) { + if (view == contentView) { + return YES; + } + } + return NO; +} + + +// 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>(cgr.origin.y); + npr.left = static_cast<uint16>(cgr.origin.x); + npr.bottom = static_cast<uint16>(CGRectGetMaxY(cgr)); + npr.right = static_cast<uint16>(CGRectGetMaxX(cgr)); +} + +#endif + +static inline void getNPRect(const NSRect& nr, NPRect& npr) +{ + npr.top = static_cast<uint16>(nr.origin.y); + npr.left = static_cast<uint16>(nr.origin.x); + npr.bottom = static_cast<uint16>(NSMaxY(nr)); + npr.right = static_cast<uint16>(NSMaxX(nr)); +} + +- (NSRect)visibleRect +{ + // WebCore may impose an additional clip (via CSS overflow or clip properties). Fetch + // that clip now. + return NSIntersectionRect([self convertRect:[element _windowClipRect] fromView:nil], [super visibleRect]); +} + +- (PortState)saveAndSetNewPortStateForUpdate:(BOOL)forUpdate +{ + ASSERT(drawingModel != NPDrawingModelCoreAnimation); + ASSERT([self currentWindow] != nil); + + // 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. + if (isDrawingModelQuickDraw(drawingModel)) + [self fixWindowPort]; + + // Use AppKit to convert view coordinates to NSWindow coordinates. + NSRect boundsInWindow = [self convertRect:[self bounds] toView:nil]; + NSRect visibleRectInWindow = [self convertRect:[self visibleRect] toView:nil]; + + // 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); + 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)) { + ::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.x = (int32)boundsInWindow.origin.x; + window.y = (int32)boundsInWindow.origin.y; + window.width = static_cast<uint32>(NSWidth(boundsInWindow)); + window.height = static_cast<uint32>(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. + NSWindow *realWindow = [self window]; + if (window.width <= 0 || window.height <= 0 || window.x < -100000 + || realWindow == nil || [realWindow isMiniaturized] + || [NSApp isHidden] + || ![self superviewsHaveSuperviews] + || [self isHiddenOrHasHiddenAncestor]) { + + // 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; + } 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)-boundsInWindow.origin.x; + nPort.qdPort.porty = (int32)-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 && !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>(-boundsInWindow.origin.x + origin.x); + nPort.qdPort.porty = static_cast<int32>(-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: { + 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; + + // Update the plugin's window/context +#ifdef NP_NO_CARBON + nPort.cgPort.window = (NPNSWindow *)[self currentWindow]; +#else + nPort.cgPort.window = eventHandler->platformWindow([self currentWindow]); +#endif /* NP_NO_CARBON */ + nPort.cgPort.context = context; + window.window = &nPort.cgPort; + + // 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; + } + + default: + ASSERT_NOT_REACHED(); + portState = NULL; + break; + } + + return portState; +} + +- (PortState)saveAndSetNewPortState +{ + return [self saveAndSetNewPortStateForUpdate:NO]; +} + +- (void)restorePortState:(PortState)portState +{ + if (drawingModel == NPDrawingModelCoreAnimation) + return; + + 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); + ASSERT(((PortState_CG *)portState)->context == nPort.cgPort.context); + CGContextRestoreGState(nPort.cgPort.context); + 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(NPP_HandleEvent); + + // 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; + + bool wasDeferring = page->defersLoading(); + if (!wasDeferring) + page->setDefersLoading(true); + + // 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) && !isTransparent && 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]; + { + JSC::JSLock::DropAllLocks dropAllLocks(false); + acceptedEvent = NPP_HandleEvent(plugin, event); + } + [self didCallPlugInFunction]; + + if (portState) { + if ([self currentWindow]) + [self restorePortState:portState]; + free(portState); + } + + if (!wasDeferring) + page->setDefersLoading(false); + + return acceptedEvent; +} + +- (void)sendActivateEvent:(BOOL)activate +{ + if (!isStarted) + return; + + eventHandler->windowFocusChanged(activate); +} + +- (void)sendDrawRectEvent:(NSRect)rect +{ + ASSERT(eventHandler); + + eventHandler->drawRect(rect); +} + +- (void)stopTimers +{ + if (eventHandler) + eventHandler->stopTimers(); + + shouldFireTimers = NO; + + if (!timers) + return; + + HashMap<uint32, PluginTimer*>::const_iterator end = timers->end(); + for (HashMap<uint32, PluginTimer*>::const_iterator it = timers->begin(); it != end; ++it) { + PluginTimer* timer = it->second; + timer->stop(); + } +} + +- (void)restartTimers +{ + ASSERT([self window]); + + if (shouldFireTimers) + [self stopTimers]; + + if (!isStarted || [[self window] isMiniaturized]) + return; + + shouldFireTimers = YES; + + // 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, PluginTimer*>::const_iterator end = timers->end(); + for (HashMap<uint32, PluginTimer*>::const_iterator it = timers->begin(); it != end; ++it) { + PluginTimer* timer = it->second; + ASSERT(!timer->isActive()); + timer->start(isCompletelyObscured); + } +} + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (void)setHasFocus:(BOOL)flag +{ + if (!isStarted) + return; + + if (hasFocus == flag) + return; + + hasFocus = flag; + + // 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); +} + +- (BOOL)becomeFirstResponder +{ + [self setHasFocus:YES]; + return YES; +} + +- (BOOL)resignFirstResponder +{ + [self setHasFocus:NO]; + return YES; +} + +// 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]; +} + +- (void)mouseDown:(NSEvent *)theEvent +{ + if (!isStarted) + return; + + eventHandler->mouseDown(theEvent); +} + +- (void)mouseUp:(NSEvent *)theEvent +{ + if (!isStarted) + return; + + eventHandler->mouseUp(theEvent); +} + +- (void)mouseEntered:(NSEvent *)theEvent +{ + if (!isStarted) + return; + + eventHandler->mouseEntered(theEvent); +} + +- (void)mouseExited:(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]; +} + +// We can't name this method mouseMoved because we don't want to override +// the NSView mouseMoved implementation. +- (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)cut:(id)sender +{ + if (!isStarted) + return; + + eventHandler->keyDown([NSApp currentEvent]); +} + +- (void)copy:(id)sender +{ + if (!isStarted) + return; + + eventHandler->keyDown([NSApp currentEvent]); +} + +- (void)paste:(id)sender +{ + if (!isStarted) + return; + + eventHandler->keyDown([NSApp currentEvent]); +} + +- (void)selectAll:(id)sender +{ + if (!isStarted) + return; + + eventHandler->keyDown([NSApp currentEvent]); +} + +#pragma mark WEB_NETSCAPE_PLUGIN + +- (BOOL)isNewWindowEqualToOldWindow +{ + ASSERT(drawingModel != NPDrawingModelCoreAnimation); + + 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; + + default: + ASSERT_NOT_REACHED(); + break; + } + + return YES; +} + +- (void)updateAndSetWindow +{ + ASSERT(drawingModel != NPDrawingModelCoreAnimation); + + // 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. QuickDraw plug-ins are an important + // excpetion to rule (3) because they manually must be 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. + if (!isStarted) + return; +#ifdef NP_NO_QUICKDRAW + if (![self canDraw]) + return; +#else + if (drawingModel != NPDrawingModelQuickDraw && ![self canDraw]) + return; +#endif // NP_NO_QUICKDRAW + + BOOL didLockFocus = [NSView focusView] != self && [self lockFocusIfCanDraw]; + + if (drawingModel == NPDrawingModelCoreGraphics || isDrawingModelQuickDraw(drawingModel)) { + [self setWindowIfNecessary]; + if (didLockFocus) + [self unlockFocus]; + + return; + } + + PortState portState = [self saveAndSetNewPortState]; + if (portState) { + [self setWindowIfNecessary]; + [self restorePortState:portState]; + free(portState); + } + if (didLockFocus) + [self unlockFocus]; +} + +- (void)setWindowIfNecessary +{ + ASSERT(drawingModel != NPDrawingModelCoreAnimation); + + 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; + ASSERT(!inSetWindow); + + inSetWindow = YES; + + // A CoreGraphics plugin's window may only be set while the plugin is being updated + ASSERT((drawingModel != NPDrawingModelCoreGraphics) || [NSView focusView] == self); + + [self willCallPlugInFunction]; + { + JSC::JSLock::DropAllLocks dropAllLocks(false); + npErr = NPP_SetWindow(plugin, &window); + } + [self didCallPlugInFunction]; + inSetWindow = NO; + +#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", + npErr, nPort.cgPort.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)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)setCurrentPluginView:(WebBaseNetscapePluginView *)view +{ + currentPluginView = view; +} + ++ (WebBaseNetscapePluginView *)currentPluginView +{ + return currentPluginView; +} + +- (BOOL)canStart +{ + return YES; +} + +- (void)didStart +{ + 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 != nil && ![sourceURL _web_isEmpty]) { + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:sourceURL]; + [request _web_setHTTPReferrer:core([self webFrame])->loader()->outgoingReferrer()]; + [self loadRequest:request inTarget:nil withNotifyData:nil sendNotification:NO]; + } +} + +- (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]; +} + +- (BOOL)start +{ + ASSERT([self currentWindow]); + + if (isStarted) + return YES; + + if (![self canStart]) + return NO; + + ASSERT([self webView]); + + if (![[[self webView] preferences] arePlugInsEnabled]) + return NO; + + // Open the plug-in package so it remains loaded while our plugin uses it + [pluginPackage 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; + + // Plug-ins are "windowed" by default. On MacOS, windowed plug-ins share the same window and graphics port as the main + // browser window. Windowless plug-ins are rendered off-screen, then copied into the main browser window. + window.type = NPWindowTypeWindow; + + NPError npErr = [self _createPlugin]; + if (npErr != NPERR_NO_ERROR) { + LOG_ERROR("NPP_New failed with error: %d", npErr); + [self _destroyPlugin]; + [pluginPackage 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); + [self _destroyPlugin]; + [pluginPackage close]; + + return NO; + } +#endif // NP_NO_CARBON + +#ifndef BUILDING_ON_TIGER + if (drawingModel == NPDrawingModelCoreAnimation) { + void *value = 0; + if (NPP_GetValue(plugin, NPPVpluginCoreAnimationLayer, &value) == NPERR_NO_ERROR && value) { + _layer = (CALayer *)value; + [self setWantsLayer:YES]; + [self setLayer:_layer]; + LOG(Plugins, "%@ is using Core Animation drawing model with layer %@", pluginPackage, _layer); + } + + ASSERT(_layer); + } +#endif + + // Create the event handler + eventHandler = WebNetscapePluginEventHandler::create(self); + + // Get the text input vtable + if (eventModel == NPEventModelCocoa) { + [self willCallPlugInFunction]; + { + JSC::JSLock::DropAllLocks dropAllLocks(false); + NPPluginTextInputFuncs *value; + if (NPP_GetValue(plugin, NPPVpluginTextInputFuncs, &value) == NPERR_NO_ERROR && value) + textInputFuncs = value; + } + [self didCallPlugInFunction]; + } + + isStarted = YES; + [[self webView] addPluginInstanceView:self]; + + if (drawingModel == NPDrawingModelCoreGraphics || isDrawingModelQuickDraw(drawingModel)) + [self updateAndSetWindow]; + + if ([self window]) { + [self addWindowObservers]; + if ([[self window] isKeyWindow]) { + [self sendActivateEvent:YES]; + } + [self restartTimers]; + } + + [self resetTrackingRect]; + + [self didStart]; + + return YES; +} + +- (void)stop +{ + // 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; + } + + [self removeTrackingRect]; + + if (!isStarted) + return; + + isStarted = NO; + + [[self webView] removePluginInstanceView:self]; + + // 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(); + + + // Stop the timers + [self stopTimers]; + + // Stop notifications and callbacks. + [self removeWindowObservers]; + [[pendingFrameLoads 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; + + [self _destroyPlugin]; + [pluginPackage close]; + + delete eventHandler; + eventHandler = 0; + + textInputFuncs = 0; +} + +- (BOOL)isStarted +{ + return isStarted; +} + +- (NPEventModel)eventModel +{ + return eventModel; +} + +- (WebDataSource *)dataSource +{ + WebFrame *webFrame = kit(core(element)->document()->frame()); + return [webFrame _dataSource]; +} + +- (WebFrame *)webFrame +{ + return [[self dataSource] webFrame]; +} + +- (WebView *)webView +{ + return [[self webFrame] webView]; +} + +- (NSWindow *)currentWindow +{ + return [self window] ? [self window] : [[self webView] hostWindow]; +} + +- (NPP)plugin +{ + return plugin; +} + +- (WebNetscapePluginPackage *)pluginPackage +{ + return pluginPackage; +} + +- (void)setPluginPackage:(WebNetscapePluginPackage *)thePluginPackage; +{ + [thePluginPackage retain]; + [pluginPackage release]; + pluginPackage = thePluginPackage; + + NPP_New = [pluginPackage NPP_New]; + NPP_Destroy = [pluginPackage NPP_Destroy]; + NPP_SetWindow = [pluginPackage NPP_SetWindow]; + NPP_NewStream = [pluginPackage NPP_NewStream]; + NPP_WriteReady = [pluginPackage NPP_WriteReady]; + NPP_Write = [pluginPackage NPP_Write]; + NPP_StreamAsFile = [pluginPackage NPP_StreamAsFile]; + NPP_DestroyStream = [pluginPackage NPP_DestroyStream]; + NPP_HandleEvent = [pluginPackage NPP_HandleEvent]; + NPP_URLNotify = [pluginPackage NPP_URLNotify]; + NPP_GetValue = [pluginPackage NPP_GetValue]; + NPP_SetValue = [pluginPackage NPP_SetValue]; + NPP_Print = [pluginPackage NPP_Print]; +} + +- (void)setMIMEType:(NSString *)theMIMEType +{ + NSString *type = [theMIMEType copy]; + [MIMEType release]; + MIMEType = type; +} + +- (void)setBaseURL:(NSURL *)theBaseURL +{ + [theBaseURL retain]; + [baseURL release]; + baseURL = theBaseURL; +} + +- (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 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 _web_URLCString]); + argsCount++; + } else { + cAttributes = (char **)malloc([keys count] * sizeof(char *)); + cValues = (char **)malloc([values count] * sizeof(char *)); + } + + BOOL isWMP = [[[pluginPackage bundle] bundleIdentifier] isEqualToString:@"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++; + } +} + +- (void)setMode:(int)theMode +{ + mode = theMode; +} + +#pragma mark NSVIEW + +- (id)initWithFrame:(NSRect)frame + pluginPackage:(WebNetscapePluginPackage *)thePluginPackage + URL:(NSURL *)theURL + baseURL:(NSURL *)theBaseURL + MIMEType:(NSString *)MIME + attributeKeys:(NSArray *)keys + attributeValues:(NSArray *)values + loadManually:(BOOL)loadManually + DOMElement:(DOMElement *)anElement +{ + [super initWithFrame:frame]; + + pendingFrameLoads = [[NSMutableDictionary alloc] init]; + + // load the plug-in if it is not already loaded + if (![thePluginPackage load]) { + [self release]; + return nil; + } + [self setPluginPackage:thePluginPackage]; + + element = [anElement retain]; + sourceURL = [theURL retain]; + + [self setMIMEType:MIME]; + [self setBaseURL:theBaseURL]; + [self setAttributeKeys:keys andValues:values]; + if (loadManually) + [self setMode:NP_FULL]; + else + [self setMode:NP_EMBED]; + + _loadManually = loadManually; + return self; +} + +- (id)initWithFrame:(NSRect)frame +{ + ASSERT_NOT_REACHED(); + return nil; +} + +- (void)fini +{ +#ifndef NP_NO_QUICKDRAW + if (offscreenGWorld) + DisposeGWorld(offscreenGWorld); +#endif + + unsigned i; + for (i = 0; i < argsCount; i++) { + free(cAttributes[i]); + free(cValues[i]); + } + free(cAttributes); + free(cValues); + + ASSERT(!eventHandler); + + if (timers) { + deleteAllValues(*timers); + delete timers; + } +} + +- (void)disconnectStream:(WebNetscapePluginStream*)stream +{ + streams.remove(stream); +} + +- (void)dealloc +{ + ASSERT(!isStarted); + + [sourceURL release]; + [_error release]; + + [pluginPackage release]; + [MIMEType release]; + [baseURL release]; + [pendingFrameLoads release]; + [element release]; + + ASSERT(!plugin); + + [self fini]; + + [super dealloc]; +} + +- (void)finalize +{ + ASSERT_MAIN_THREAD(); + ASSERT(!isStarted); + + [self fini]; + + [super finalize]; +} + +- (void)drawRect:(NSRect)rect +{ + if (drawingModel == NPDrawingModelCoreAnimation) + return; + + if (!isStarted) + return; + + if ([NSGraphicsContext currentContextDrawingToScreen]) + [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); + } + } +} + +- (BOOL)isFlipped +{ + return YES; +} + +- (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. + [self _viewHasMoved]; +} + +-(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)viewWillMoveToWindow:(NSWindow *)newWindow +{ + if (isDrawingModelQuickDraw(drawingModel)) + [self tellQuickTimeToChill]; + + // 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 WebPreferencesChangedNotification -- 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:WebPreferencesChangedNotification 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 WebPreferencesChangedNotification -- 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:WebPreferencesChangedNotification object:nil]; + } +} + +- (void)viewDidMoveToWindow +{ + [self resetTrackingRect]; + + if ([self window]) { + // While in the view hierarchy, observe WebPreferencesChangedNotification so that we can start/stop depending + // on whether plugins are enabled. + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(preferencesHaveChanged:) + name:WebPreferencesChangedNotification + object:nil]; + + // View moved to an actual window. Start it if not already started. + [self start]; + [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 WebPreferencesChangedNotification observer -- we will observe once again when we move back into the window + [[NSNotificationCenter defaultCenter] removeObserver:self name:WebPreferencesChangedNotification object:nil]; + } +} + +- (void)viewDidMoveToHostWindow +{ + if ([[self webView] hostWindow]) { + // View now has an associated window. Start it if not already started. + [self start]; + } +} + +#pragma mark NOTIFICATIONS + +- (void)windowWillClose:(NSNotification *)notification +{ + [self stop]; +} + +- (void)windowBecameKey:(NSNotification *)notification +{ + [self sendActivateEvent:YES]; + [self setNeedsDisplay:YES]; + [self restartTimers]; +#ifndef NP_NO_CARBON + SetUserFocusWindow((WindowRef)[[self window] windowRef]); +#endif // NP_NO_CARBON +} + +- (void)windowResignedKey:(NSNotification *)notification +{ + [self sendActivateEvent:NO]; + [self setNeedsDisplay:YES]; + [self restartTimers]; +} + +- (void)windowDidMiniaturize:(NSNotification *)notification +{ + [self stopTimers]; +} + +- (void)windowDidDeminiaturize:(NSNotification *)notification +{ + [self stopTimers]; +} + +- (void)loginWindowDidSwitchFromUser:(NSNotification *)notification +{ + [self stopTimers]; +} + +-(void)loginWindowDidSwitchToUser:(NSNotification *)notification +{ + [self restartTimers]; +} + +- (void)preferencesHaveChanged:(NSNotification *)notification +{ + WebPreferences *preferences = [[self webView] preferences]; + BOOL arePlugInsEnabled = [preferences arePlugInsEnabled]; + + if ([notification object] == preferences && isStarted != arePlugInsEnabled) { + if (arePlugInsEnabled) { + if ([self currentWindow]) { + [self start]; + } + } else { + [self stop]; + [self setNeedsDisplay:YES]; + } + } +} + +- (NPObject *)createPluginScriptableObject +{ + if (!NPP_GetValue || ![self isStarted]) + return NULL; + + NPObject *value = NULL; + NPError error; + [self willCallPlugInFunction]; + { + JSC::JSLock::DropAllLocks dropAllLocks(false); + error = NPP_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 (![self isStarted]) + return; + + if (!_manualStream->plugin()) { + + _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 retain]; + [_error release]; + _error = error; + + if (![self isStarted]) { + return; + } + + _manualStream->destroyStreamWithError(error); +} + +- (void)pluginViewFinishedLoading:(NSView *)pluginView +{ + ASSERT(_loadManually); + ASSERT(_manualStream); + + if ([self isStarted]) + _manualStream->didFinishLoading(0); +} + +#pragma mark NSTextInput implementation + +- (NSTextInputContext *)inputContext +{ +#ifndef NP_NO_CARBON + if (![self isStarted] || eventModel == NPEventModelCarbon) + return nil; +#endif + + return [super inputContext]; +} + +- (BOOL)hasMarkedText +{ + ASSERT(eventModel == NPEventModelCocoa); + ASSERT([self isStarted]); + + if (textInputFuncs && textInputFuncs->hasMarkedText) + return textInputFuncs->hasMarkedText(plugin); + + return NO; +} + +- (void)insertText:(id)aString +{ + ASSERT(eventModel == NPEventModelCocoa); + ASSERT([self isStarted]); + + if (textInputFuncs && textInputFuncs->insertText) + textInputFuncs->insertText(plugin, aString); +} + +- (NSRange)markedRange +{ + ASSERT(eventModel == NPEventModelCocoa); + ASSERT([self isStarted]); + + if (textInputFuncs && textInputFuncs->markedRange) + return textInputFuncs->markedRange(plugin); + + return NSMakeRange(NSNotFound, 0); +} + +- (NSRange)selectedRange +{ + ASSERT(eventModel == NPEventModelCocoa); + ASSERT([self isStarted]); + + if (textInputFuncs && textInputFuncs->selectedRange) + return textInputFuncs->selectedRange(plugin); + + return NSMakeRange(NSNotFound, 0); +} + +- (void)setMarkedText:(id)aString selectedRange:(NSRange)selRange +{ + ASSERT(eventModel == NPEventModelCocoa); + ASSERT([self isStarted]); + + if (textInputFuncs && textInputFuncs->setMarkedText) + textInputFuncs->setMarkedText(plugin, aString, selRange); +} + +- (void)unmarkText +{ + ASSERT(eventModel == NPEventModelCocoa); + ASSERT([self isStarted]); + + if (textInputFuncs && textInputFuncs->unmarkText) + textInputFuncs->unmarkText(plugin); +} + +- (NSArray *)validAttributesForMarkedText +{ + ASSERT(eventModel == NPEventModelCocoa); + ASSERT([self isStarted]); + + if (textInputFuncs && textInputFuncs->validAttributesForMarkedText) + return textInputFuncs->validAttributesForMarkedText(plugin); + + return [NSArray array]; +} + +- (NSAttributedString *)attributedSubstringFromRange:(NSRange)theRange +{ + ASSERT(eventModel == NPEventModelCocoa); + ASSERT([self isStarted]); + + if (textInputFuncs && textInputFuncs->attributedSubstringFromRange) + return textInputFuncs->attributedSubstringFromRange(plugin, theRange); + + return nil; +} + +- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint +{ + ASSERT(eventModel == NPEventModelCocoa); + ASSERT([self isStarted]); + + if (textInputFuncs && textInputFuncs->characterIndexForPoint) { + // Convert the point to window coordinates + NSPoint point = [[self window] convertScreenToBase:thePoint]; + + // And view coordinates + point = [self convertPoint:point fromView:nil]; + + return textInputFuncs->characterIndexForPoint(plugin, point); + } + + return NSNotFound; +} + +- (void)doCommandBySelector:(SEL)aSelector +{ + ASSERT(eventModel == NPEventModelCocoa); + ASSERT([self isStarted]); + + if (textInputFuncs && textInputFuncs->doCommandBySelector) + textInputFuncs->doCommandBySelector(plugin, aSelector); +} + +- (NSRect)firstRectForCharacterRange:(NSRange)theRange +{ + ASSERT(eventModel == NPEventModelCocoa); + ASSERT([self isStarted]); + + if (textInputFuncs && textInputFuncs->firstRectForCharacterRange) { + NSRect rect = textInputFuncs->firstRectForCharacterRange(plugin, theRange); + + // Convert the rect to window coordinates + rect = [self convertRect:rect toView:nil]; + + // Convert the rect location to screen coordinates + rect.origin = [[self window] convertBaseToScreen:rect.origin]; + + return rect; + } + + return NSZeroRect; +} + +// test for 10.4 because of <rdar://problem/4243463> +#ifdef BUILDING_ON_TIGER +- (long)conversationIdentifier +{ + return (long)self; +} +#else +- (NSInteger)conversationIdentifier +{ + return (NSInteger)self; +} +#endif + +@end + +@implementation WebBaseNetscapePluginView (WebNPPCallbacks) + +- (NSMutableURLRequest *)requestWithURLCString:(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]; + CFRelease(string); + 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; +} + +- (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(false); + NPP_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 objectForKey:webFrame]; + ASSERT(pluginRequest != nil); + ASSERT([pluginRequest sendNotification]); + + [self willCallPlugInFunction]; + { + JSC::JSLock::DropAllLocks dropAllLocks(false); + NPP_URLNotify(plugin, [[[pluginRequest request] URL] _web_URLCString], reason, [pluginRequest notifyData]); + } + [self didCallPlugInFunction]; + + [pendingFrameLoads 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(false); + NPP_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. + WebBaseNetscapePluginView *view = [frame _internalLoadDelegate]; + if (view != nil) { + ASSERT([view isKindOfClass:[WebBaseNetscapePluginView class]]); + [view webFrame:frame didFinishLoadWithReason:NPRES_USER_BREAK]; + } + [pendingFrameLoads _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 (!FrameLoader::canLoad(URL, String(), core([self webFrame])->document())) + 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 +{ + return [[[self webView] userAgentForURL:baseURL] UTF8String]; +} + +-(void)status:(const char *)message +{ + if (!message) { + LOG_ERROR("NPN_Status passed a NULL status message"); + return; + } + + CFStringRef status = CFStringCreateWithCString(NULL, 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 setNeedsDisplayInRect:NSMakeRect(invalidRect->left, invalidRect->top, + (float)invalidRect->right - invalidRect->left, (float)invalidRect->bottom - invalidRect->top)]; +} + +-(BOOL)isOpaque +{ + return YES; +} + +- (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 setNeedsDisplayInRect:invalidRect]; +} + +-(void)forceRedraw +{ + LOG(Plugins, "forceRedraw"); + [self setNeedsDisplay:YES]; + [[self window] displayIfNeeded]; +} + +static NPBrowserTextInputFuncs *browserTextInputFuncs() +{ + static NPBrowserTextInputFuncs inputFuncs = { + 0, + sizeof(NPBrowserTextInputFuncs), + NPN_MarkedTextAbandoned, + NPN_MarkedTextSelectionChanged + }; + + return &inputFuncs; +} + +- (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 = (NPObject *)[element _NPObject]; + + // 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 NPNVbrowserTextInputFuncs: + { + if (eventModel == NPEventModelCocoa) { + *(NPBrowserTextInputFuncs **)value = browserTextInputFuncs(); + return NPERR_NO_ERROR; + } + } + default: + break; + } + + return NPERR_GENERIC_ERROR; +} + +- (NPError)setVariable:(NPPVariable)variable value:(void *)value +{ + switch (variable) { + case NPPVpluginWindowBool: + { + NPWindowType newWindowType = (value ? NPWindowTypeWindow : NPWindowTypeDrawable); + + // Redisplay if window type is changing (some drawing models can only have their windows set while updating). + if (newWindowType != window.type) + [self setNeedsDisplay:YES]; + + window.type = newWindowType; + } + + case NPPVpluginTransparentBool: + { + BOOL newTransparent = (value != 0); + + // Redisplay if transparency is changing + if (isTransparent != newTransparent) + [self setNeedsDisplay:YES]; + + isTransparent = newTransparent; + + return NPERR_NO_ERROR; + } + + 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: + drawingModel = newDrawingModel; + return NPERR_NO_ERROR; + + case NPDrawingModelCoreAnimation: + drawingModel = newDrawingModel; + return NPERR_NO_ERROR; + + + // Unsupported (or unknown) drawing models: + default: + LOG(Plugins, "Plugin %@ uses unsupported drawing model: %d", pluginPackage, 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", pluginPackage, eventModel); + return NPERR_GENERIC_ERROR; + } + } + + default: + return NPERR_GENERIC_ERROR; + } +} + +- (uint32)scheduleTimerWithInterval:(uint32)interval repeat:(NPBool)repeat timerFunc:(void (*)(NPP npp, uint32 timerID))timerFunc +{ + if (!timerFunc) + return 0; + + if (!timers) + timers = new HashMap<uint32, PluginTimer*>; + + uint32 timerID = ++currentTimerID; + + PluginTimer* timer = new PluginTimer(plugin, timerID, interval, repeat, timerFunc); + timers->set(timerID, timer); + + if (shouldFireTimers) + timer->start(isCompletelyObscured); + + return 0; +} + +- (void)unscheduleTimer:(uint32)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; +} + +@end + +@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 + +@implementation WebBaseNetscapePluginView (Internal) + +- (NPError)_createPlugin +{ + plugin = (NPP)calloc(1, sizeof(NPP_t)); + plugin->ndata = self; + + ASSERT(NPP_New); + + // NPN_New(), which creates the plug-in instance, should never be called while calling a plug-in function for that instance. + ASSERT(pluginFunctionCallDepth == 0); + + Frame* frame = core([self webFrame]); + if (!frame) + return NPERR_GENERIC_ERROR; + Page* page = frame->page(); + if (!page) + return NPERR_GENERIC_ERROR; + + bool wasDeferring = page->defersLoading(); + if (!wasDeferring) + page->setDefersLoading(true); + + PluginMainThreadScheduler::scheduler().registerPlugin(plugin); + + [[self class] setCurrentPluginView:self]; + NPError npErr = NPP_New((char *)[MIMEType cString], plugin, mode, argsCount, cAttributes, cValues, NULL); + [[self class] setCurrentPluginView:nil]; + + if (!wasDeferring) + page->setDefersLoading(false); + + LOG(Plugins, "NPP_New: %d", npErr); + return npErr; +} + +- (void)_destroyPlugin +{ + PluginMainThreadScheduler::scheduler().unregisterPlugin(plugin); + + NPError npErr; + npErr = NPP_Destroy(plugin, NULL); + LOG(Plugins, "NPP_Destroy: %d", npErr); + + if (Frame* frame = core([self webFrame])) + frame->script()->cleanupScriptObjectsForPlugin(self); + + free(plugin); + plugin = NULL; +} + +- (void)_viewHasMoved +{ + // 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; + + if (isDrawingModelQuickDraw(drawingModel)) + [self tellQuickTimeToChill]; + + if (drawingModel == NPDrawingModelCoreGraphics || isDrawingModelQuickDraw(drawingModel)) + [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]; +} + +- (NSBitmapImageRep *)_printedPluginBitmap +{ +#ifdef NP_NO_QUICKDRAW + return nil; +#else + // Cannot print plugins that do not implement NPP_Print + if (!NPP_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(false); + NPP_Print(plugin, &npPrint); + } + [self didCallPlugInFunction]; + + // Don't need the GWorld anymore + DisposeGWorld(printGWorld); + + return bitmap; +#endif +} + +- (void)_redeliverStream +{ + if ([self dataSource] && [self 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]; + else + [self pluginViewFinishedLoading:self]; + } + } + } +} + +@end + +@implementation NSData (PluginExtras) + +- (BOOL)_web_startsWithBlankLine +{ + return [self length] > 0 && ((const char *)[self bytes])[0] == '\n'; +} + + +- (NSInteger)_web_locationAfterFirstBlankLine +{ + const char *bytes = (const char *)[self bytes]; + unsigned length = [self length]; + + unsigned i; + for (i = 0; i < length - 4; i++) { + + // Support for Acrobat. It sends "\n\n". + if (bytes[i] == '\n' && bytes[i+1] == '\n') { + return i+2; + } + + // Returns the position after 2 CRLF's or 1 CRLF if it is the first line. + if (bytes[i] == '\r' && bytes[i+1] == '\n') { + i += 2; + if (i == 2) { + return i; + } else if (bytes[i] == '\n') { + // Support for Director. It sends "\r\n\n" (3880387). + return i+1; + } else if (bytes[i] == '\r' && bytes[i+1] == '\n') { + // Support for Flash. It sends "\r\n\r\n" (3758113). + return i+2; + } + } + } + return NSNotFound; +} + +@end +#endif diff --git a/WebKit/mac/Plugins/WebBasePluginPackage.h b/WebKit/mac/Plugins/WebBasePluginPackage.h new file mode 100644 index 0000000..1082551 --- /dev/null +++ b/WebKit/mac/Plugins/WebBasePluginPackage.h @@ -0,0 +1,107 @@ +/* + * 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/WebCoreViewFactory.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 <WebCorePluginInfo> +{ + NSMutableSet *pluginDatabases; + + NSString *name; + NSString *path; + NSString *pluginDescription; + + NSBundle *bundle; + CFBundleRef cfBundle; + + NSDictionary *MIMEToDescription; + NSDictionary *MIMEToExtensions; + NSMutableDictionary *extensionToMIME; + + BP_CreatePluginMIMETypesPreferencesFuncPtr BP_CreatePluginMIMETypesPreferences; +} + ++ (WebBasePluginPackage *)pluginWithPath:(NSString *)pluginPath; +- (id)initWithPath:(NSString *)pluginPath; + +- (BOOL)getPluginInfoFromPLists; + +- (BOOL)load; +- (void)unload; + +- (NSString *)name; +- (NSString *)path; +- (NSString *)filename; +- (NSString *)pluginDescription; +- (NSBundle *)bundle; + +- (NSEnumerator *)extensionEnumerator; +- (NSEnumerator *)MIMETypeEnumerator; +- (NSString *)descriptionForMIMEType:(NSString *)MIMEType; +- (NSString *)MIMETypeForExtension:(NSString *)extension; +- (NSArray *)extensionsForMIMEType:(NSString *)MIMEType; + +- (void)setName:(NSString *)theName; +- (void)setPath:(NSString *)thePath; +- (void)setPluginDescription:(NSString *)description; +- (void)setMIMEToDescriptionDictionary:(NSDictionary *)MIMEToDescriptionDictionary; +- (void)setMIMEToExtensionsDictionary:(NSDictionary *)MIMEToExtensionsDictionary; + +- (BOOL)isQuickTimePlugIn; +- (BOOL)isJavaPlugIn; + +- (BOOL)isNativeLibraryData:(NSData *)data; +- (UInt32)versionNumber; +- (void)wasAddedToPluginDatabase:(WebPluginDatabase *)database; +- (void)wasRemovedFromPluginDatabase:(WebPluginDatabase *)database; + +@end diff --git a/WebKit/mac/Plugins/WebBasePluginPackage.m b/WebKit/mac/Plugins/WebBasePluginPackage.m new file mode 100644 index 0000000..03d438b --- /dev/null +++ b/WebKit/mac/Plugins/WebBasePluginPackage.m @@ -0,0 +1,523 @@ +/* + * 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 <WebKit/WebKitNSStringExtras.h> +#import <WebKit/WebNetscapePluginPackage.h> +#import <WebKit/WebNSObjectExtras.h> +#import <WebKit/WebPluginPackage.h> +#import <WebCore/WebCoreObjCExtras.h> +#import <wtf/Assertions.h> +#import <wtf/Vector.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; + +@implementation WebBasePluginPackage + +#ifndef BUILDING_ON_TIGER ++ (void)initialize +{ + 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)); +} + +- (NSString *)pathByResolvingSymlinksAndAliasesInPath:(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 = [[self pathByResolvingSymlinksAndAliasesInPath:pluginPath] retain]; + bundle = [[NSBundle alloc] initWithPath:path]; +#ifndef __ppc__ + // 32-bit PowerPC is the only platform where non-bundled CFM plugins are supported + if (!bundle) { + [self release]; + return nil; + } +#endif + cfBundle = CFBundleCreate(NULL, (CFURLRef)[NSURL fileURLWithPath:path]); + extensionToMIME = [[NSMutableDictionary alloc] init]; + + return self; +} + +- (BOOL)getPluginInfoFromBundleAndMIMEDictionary:(NSDictionary *)MIMETypes +{ + if (!bundle) + return NO; + + if (!MIMETypes) { + MIMETypes = [bundle objectForInfoDictionaryKey:WebPluginMIMETypesKey]; + if (!MIMETypes) + return NO; + } + + NSMutableDictionary *MIMEToExtensionsDictionary = [NSMutableDictionary dictionary]; + NSMutableDictionary *MIMEToDescriptionDictionary = [NSMutableDictionary dictionary]; + 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; + + extensions = [[MIMEDictionary objectForKey:WebPluginExtensionsKey] _web_lowercaseStrings]; + if ([extensions count] == 0) + extensions = [NSArray arrayWithObject:@""]; + + MIME = [MIME lowercaseString]; + + [MIMEToExtensionsDictionary setObject:extensions forKey:MIME]; + + description = [MIMEDictionary objectForKey:WebPluginTypeDescriptionKey]; + if (!description) + description = @""; + + [MIMEToDescriptionDictionary setObject:description forKey:MIME]; + } + + [self setMIMEToExtensionsDictionary:MIMEToExtensionsDictionary]; + [self setMIMEToDescriptionDictionary:MIMEToDescriptionDictionary]; + + NSString *filename = [self filename]; + + NSString *theName = [bundle objectForInfoDictionaryKey:WebPluginNameKey]; + if (!theName) + theName = filename; + [self setName:theName]; + + description = [bundle objectForInfoDictionaryKey:WebPluginDescriptionKey]; + if (!description) + description = filename; + [self setPluginDescription:description]; + + return YES; +} + +- (void)unload +{ +} + +- (NSDictionary *)pListForPath:(NSString *)pListPath createFile:(BOOL)createFile +{ + if (createFile && [self load] && BP_CreatePluginMIMETypesPreferences) { + BP_CreatePluginMIMETypesPreferences(); + [self unload]; + } + + NSDictionary *pList = nil; + NSData *data = [NSData dataWithContentsOfFile:pListPath]; + if (data) { + pList = [NSPropertyListSerialization propertyListFromData:data + mutabilityOption:NSPropertyListImmutable + format:nil + errorDescription:nil]; + } + + return pList; +} + +- (BOOL)getPluginInfoFromPLists +{ + if (!bundle) + return NO; + + NSDictionary *MIMETypes = nil; + NSString *pListFilename = [bundle 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]; + } + + // Pass the MIME dictionary to the superclass to parse it. + return [self getPluginInfoFromBundleAndMIMEDictionary:MIMETypes]; +} + +- (BOOL)load +{ + if (bundle && !BP_CreatePluginMIMETypesPreferences) + BP_CreatePluginMIMETypesPreferences = (BP_CreatePluginMIMETypesPreferencesFuncPtr)CFBundleGetFunctionPointerForName(cfBundle, CFSTR("BP_CreatePluginMIMETypesPreferences")); + + return YES; +} + +- (void)dealloc +{ + ASSERT(!pluginDatabases || [pluginDatabases count] == 0); + [pluginDatabases release]; + + [name release]; + [path release]; + [pluginDescription release]; + + [MIMEToDescription release]; + [MIMEToExtensions release]; + [extensionToMIME release]; + + [bundle release]; + if (cfBundle) + CFRelease(cfBundle); + + [super dealloc]; +} + +- (void)finalize +{ + ASSERT_MAIN_THREAD(); + ASSERT(!pluginDatabases || [pluginDatabases count] == 0); + [pluginDatabases release]; + + if (cfBundle) + CFRelease(cfBundle); + + [super finalize]; +} + +- (NSString *)name +{ + return name; +} + +- (NSString *)path +{ + return path; +} + +- (NSString *)filename +{ + return [path lastPathComponent]; +} + +- (NSString *)pluginDescription +{ + return pluginDescription; +} + +- (NSEnumerator *)extensionEnumerator +{ + return [extensionToMIME keyEnumerator]; +} + +- (NSEnumerator *)MIMETypeEnumerator +{ + return [MIMEToExtensions keyEnumerator]; +} + +- (NSString *)descriptionForMIMEType:(NSString *)MIMEType +{ + return [MIMEToDescription objectForKey:MIMEType]; +} + +- (NSString *)MIMETypeForExtension:(NSString *)extension +{ + return [extensionToMIME objectForKey:extension]; +} + +- (NSArray *)extensionsForMIMEType:(NSString *)MIMEType +{ + return [MIMEToExtensions objectForKey:MIMEType]; +} + +- (NSBundle *)bundle +{ + return bundle; +} + +- (void)setName:(NSString *)theName +{ + [name release]; + name = [theName retain]; +} + +- (void)setPath:(NSString *)thePath +{ + [path release]; + path = [thePath retain]; +} + +- (void)setPluginDescription:(NSString *)description +{ + [pluginDescription release]; + pluginDescription = [description retain]; +} + +- (void)setMIMEToDescriptionDictionary:(NSDictionary *)MIMEToDescriptionDictionary +{ + [MIMEToDescription release]; + MIMEToDescription = [MIMEToDescriptionDictionary retain]; +} + +- (void)setMIMEToExtensionsDictionary:(NSDictionary *)MIMEToExtensionsDictionary +{ + [MIMEToExtensions release]; + MIMEToExtensions = [MIMEToExtensionsDictionary retain]; + + // Reverse the mapping + [extensionToMIME removeAllObjects]; + + NSEnumerator *MIMEEnumerator = [MIMEToExtensions keyEnumerator], *extensionEnumerator; + NSString *MIME, *extension; + NSArray *extensions; + + while ((MIME = [MIMEEnumerator nextObject]) != nil) { + extensions = [MIMEToExtensions objectForKey:MIME]; + extensionEnumerator = [extensions objectEnumerator]; + + while ((extension = [extensionEnumerator nextObject]) != nil) { + if (![extension isEqualToString:@""]) + [extensionToMIME setObject:MIME forKey:extension]; + } + } +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"name: %@\npath: %@\nmimeTypes:\n%@\npluginDescription:%@", + name, path, [MIMEToExtensions description], [MIMEToDescription description], pluginDescription]; +} + +- (BOOL)isQuickTimePlugIn +{ + NSString *bundleIdentifier = [[self bundle] bundleIdentifier]; + return [bundleIdentifier _webkit_isCaseInsensitiveEqualToString:QuickTimeCarbonPluginIdentifier] || + [bundleIdentifier _webkit_isCaseInsensitiveEqualToString:QuickTimeCocoaPluginIdentifier]; +} + +- (BOOL)isJavaPlugIn +{ + NSString *bundleIdentifier = [[self bundle] bundleIdentifier]; + return [bundleIdentifier _webkit_isCaseInsensitiveEqualToString:JavaCocoaPluginIdentifier] || + [bundleIdentifier _webkit_isCaseInsensitiveEqualToString:JavaCarbonPluginIdentifier] || + [[path lastPathComponent] _webkit_isCaseInsensitiveEqualToString:JavaCFMPluginFilename]; +} + +static inline void swapIntsInHeader(uint8_t* bytes, unsigned length) +{ + for (unsigned i = 0; i < length; i += 4) + *(uint32_t*)(bytes + i) = OSSwapInt32(*(uint32_t *)(bytes + i)); +} + +- (BOOL)isNativeLibraryData:(NSData *)data +{ + Vector<uint8_t, 512> bytes([data length]); + memcpy(bytes.data(), [data bytes], bytes.size()); + + unsigned numArchs = 0; + struct fat_arch singleArch = { 0, 0, 0, 0, 0 }; + struct fat_arch* archs = 0; + + if (bytes.size() >= sizeof(struct mach_header_64)) { + uint32_t magic = *reinterpret_cast<uint32_t*>(bytes.data()); + + if (magic == MH_MAGIC || magic == MH_CIGAM) { + // We have a 32-bit thin binary + struct mach_header* header = (struct mach_header*)bytes.data(); + + // Check if we need to swap the bytes + if (magic == MH_CIGAM) + swapIntsInHeader(bytes.data(), bytes.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*)bytes.data(); + + // Check if we need to swap the bytes + if (magic == MH_CIGAM_64) + swapIntsInHeader(bytes.data(), bytes.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(bytes.data(), bytes.size()); + + archs = (struct fat_arch*)(bytes.data() + sizeof(struct fat_header)); + numArchs = ((struct fat_header *)bytes.data())->nfat_arch; + + unsigned maxArchs = (bytes.size() - 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); +} + +- (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]; +} + +@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/WebKit/mac/Plugins/WebJavaPlugIn.h b/WebKit/mac/Plugins/WebJavaPlugIn.h new file mode 100644 index 0000000..5a1be6d --- /dev/null +++ b/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/WebKit/mac/Plugins/WebKitPluginContainerView.h b/WebKit/mac/Plugins/WebKitPluginContainerView.h new file mode 100644 index 0000000..a67c105 --- /dev/null +++ b/WebKit/mac/Plugins/WebKitPluginContainerView.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Cocoa/Cocoa.h> + +@class DOMElement; + +@interface WebKitPluginContainerView : NSView +{ + DOMElement *_element; +} + +- (id)initWithFrame:(NSRect)r + DOMElement:(DOMElement *)anElement; + +- (id)objectForWebScript; + +@end diff --git a/WebKit/mac/Plugins/WebKitPluginContainerView.mm b/WebKit/mac/Plugins/WebKitPluginContainerView.mm new file mode 100644 index 0000000..fb61644 --- /dev/null +++ b/WebKit/mac/Plugins/WebKitPluginContainerView.mm @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "WebKitPluginContainerView.h" +#import <WebKit/DOMPrivate.h> + +@implementation WebKitPluginContainerView + +- (id)initWithFrame:(NSRect)frame + DOMElement:(DOMElement *)anElement +{ + [super initWithFrame:frame]; + + _element = [anElement retain]; + + return self; +} + +- (void)dealloc +{ + [_element release]; + + [super dealloc]; +} + +- (NSRect)visibleRect +{ + if (![self window]) + return [super visibleRect]; + + // WebCore may impose an additional clip (via CSS overflow or clip properties). Fetch + // that clip now. + return NSIntersectionRect([self convertRect:[_element _windowClipRect] fromView:nil], [super visibleRect]); +} + +- (BOOL)respondsToSelector:(SEL)selector +{ + if (selector == @selector(objectForWebScript)) + return [[[self subviews] objectAtIndex: 0] respondsToSelector:selector]; + return [super respondsToSelector:selector]; +} + +- (id)objectForWebScript +{ + return [[[self subviews] objectAtIndex: 0] objectForWebScript]; +} + +@end + diff --git a/WebKit/mac/Plugins/WebNetscapeDeprecatedFunctions.c b/WebKit/mac/Plugins/WebNetscapeDeprecatedFunctions.c new file mode 100644 index 0000000..797e824 --- /dev/null +++ b/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/WebKit/mac/Plugins/WebNetscapeDeprecatedFunctions.h b/WebKit/mac/Plugins/WebNetscapeDeprecatedFunctions.h new file mode 100644 index 0000000..93c3194 --- /dev/null +++ b/WebKit/mac/Plugins/WebNetscapeDeprecatedFunctions.h @@ -0,0 +1,35 @@ +/* + * 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> + +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); + +#endif /* ENABLE(NETSCAPE_PLUGIN_API) && !defined(__LP64__) */ diff --git a/WebKit/mac/Plugins/WebNetscapePluginEventHandler.h b/WebKit/mac/Plugins/WebNetscapePluginEventHandler.h new file mode 100644 index 0000000..54402bb --- /dev/null +++ b/WebKit/mac/Plugins/WebNetscapePluginEventHandler.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. + */ + +#ifndef WebNetscapePluginEventHandler_h +#define WebNetscapePluginEventHandler_h + +#import "WebBaseNetscapePluginView.h" + +#if ENABLE(NETSCAPE_PLUGIN_API) + +@class NSEvent; +@class WebBaseNetscapePluginView; + +struct CGRect; + +class WebNetscapePluginEventHandler { +public: + static WebNetscapePluginEventHandler* create(WebBaseNetscapePluginView*); + virtual ~WebNetscapePluginEventHandler() { } + + virtual void drawRect(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 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(WebBaseNetscapePluginView* pluginView) + : m_pluginView(pluginView) + , m_currentEventIsUserGesture(false) + { + } + + WebBaseNetscapePluginView* m_pluginView; + bool m_currentEventIsUserGesture; +}; + +#endif // ENABLE(NETSCAPE_PLUGIN_API) + +#endif // WebNetscapePluginEventHandler_h + + diff --git a/WebKit/mac/Plugins/WebNetscapePluginEventHandler.mm b/WebKit/mac/Plugins/WebNetscapePluginEventHandler.mm new file mode 100644 index 0000000..e8e6d8a --- /dev/null +++ b/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 "WebBaseNetscapePluginView.h" +#import "WebNetscapePluginEventHandlerCarbon.h" +#import "WebNetscapePluginEventHandlerCocoa.h" + +WebNetscapePluginEventHandler* WebNetscapePluginEventHandler::create(WebBaseNetscapePluginView* 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/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCarbon.h b/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCarbon.h new file mode 100644 index 0000000..cf26276 --- /dev/null +++ b/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCarbon.h @@ -0,0 +1,81 @@ +/* + * 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(WebBaseNetscapePluginView*); + + virtual void drawRect(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 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/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCarbon.mm b/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCarbon.mm new file mode 100644 index 0000000..bfdd91c --- /dev/null +++ b/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCarbon.mm @@ -0,0 +1,411 @@ +/* + * 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 "WebBaseNetscapePluginView.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(WebBaseNetscapePluginView* 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->where.h = static_cast<short>(carbonEvent->where.h * HIGetScaleFactor()); + carbonEvent->where.v = static_cast<short>(carbonEvent->where.v * HIGetScaleFactor()); + 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)) { + carbonEvent->where.h = static_cast<short>(carbonEvent->where.h * HIGetScaleFactor()); + carbonEvent->where.v = static_cast<short>(carbonEvent->where.v * HIGetScaleFactor()); + 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(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 = 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 = adjustCursorEvent; + + BOOL acceptedEvent; + acceptedEvent = sendEvent(&event); + + LOG(PluginEvents, "NPP_HandleEvent(mouseExited): %d", acceptedEvent); +} + +void WebNetscapePluginEventHandlerCarbon::mouseDragged(NSEvent*) +{ +} + +void WebNetscapePluginEventHandlerCarbon::mouseMoved(NSEvent*) +{ +} + +void WebNetscapePluginEventHandlerCarbon::keyDown(NSEvent *theEvent) +{ + m_suspendKeyUpEvents = true; + WKSendKeyEventToTSM(theEvent); +} + +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 = getFocusEvent; + acceptedEvent = sendEvent(&event); + LOG(PluginEvents, "NPP_HandleEvent(getFocusEvent): %d", acceptedEvent); + installKeyEventHandler(); + } else { + event.what = loseFocusEvent; + acceptedEvent = sendEvent(&event); + LOG(PluginEvents, "NPP_HandleEvent(loseFocusEvent): %d", acceptedEvent); + removeKeyEventHandler(); + } +} + +void WebNetscapePluginEventHandlerCarbon::windowFocusChanged(bool hasFocus) +{ + EventRecord event; + + getCarbonEvent(&event); + event.what = activateEvt; + WindowRef windowRef = (WindowRef)[[m_pluginView window] windowRef]; + 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/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCocoa.h b/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCocoa.h new file mode 100644 index 0000000..e22ff3d --- /dev/null +++ b/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCocoa.h @@ -0,0 +1,65 @@ +/* + * 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(WebBaseNetscapePluginView*); + + virtual void drawRect(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 windowFocusChanged(bool hasFocus); + virtual void focusChanged(bool hasFocus); + + virtual void* platformWindow(NSWindow*); +private: + bool sendMouseEvent(NSEvent*, NPCocoaEventType); + bool sendKeyEvent(NSEvent*, NPCocoaEventType); + bool sendEvent(NPCocoaEvent*); +}; + +#endif //WebNetscapePluginEventHandlerCocoa_h + +#endif // ENABLE(NETSCAPE_PLUGIN_API) + diff --git a/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCocoa.mm b/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCocoa.mm new file mode 100644 index 0000000..fb13a12 --- /dev/null +++ b/WebKit/mac/Plugins/WebNetscapePluginEventHandlerCocoa.mm @@ -0,0 +1,205 @@ +/* + * 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 "WebBaseNetscapePluginView.h" + +WebNetscapePluginEventHandlerCocoa::WebNetscapePluginEventHandlerCocoa(WebBaseNetscapePluginView* pluginView) + : WebNetscapePluginEventHandler(pluginView) +{ +} + +static inline void initializeEvent(NPCocoaEvent* event, NPCocoaEventType type) +{ + event->type = type; + event->version = 0; +} + +void WebNetscapePluginEventHandlerCocoa::drawRect(const NSRect& rect) +{ + NPCocoaEvent event; + + initializeEvent(&event, NPCocoaEventDrawRect); + 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; + + 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); + + // If the plug-in did not handle the event, pass it on to the Input Manager. + if (!retval) + [m_pluginView interpretKeyEvents:[NSArray arrayWithObject:event]]; +} + +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); +} + +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); +} + +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; +} + +#endif // ENABLE(NETSCAPE_PLUGIN_API) diff --git a/WebKit/mac/Plugins/WebNetscapePluginPackage.h b/WebKit/mac/Plugins/WebNetscapePluginPackage.h new file mode 100644 index 0000000..6ccbfdb --- /dev/null +++ b/WebKit/mac/Plugins/WebNetscapePluginPackage.h @@ -0,0 +1,105 @@ +/* + * 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 pluginSize; + uint16 pluginVersion; + + ResFileRefNum resourceRef; + + NPP_NewProcPtr NPP_New; + NPP_DestroyProcPtr NPP_Destroy; + NPP_SetWindowProcPtr NPP_SetWindow; + NPP_NewStreamProcPtr NPP_NewStream; + NPP_DestroyStreamProcPtr NPP_DestroyStream; + NPP_StreamAsFileProcPtr NPP_StreamAsFile; + NPP_WriteReadyProcPtr NPP_WriteReady; + NPP_WriteProcPtr NPP_Write; + NPP_PrintProcPtr NPP_Print; + NPP_HandleEventProcPtr NPP_HandleEvent; + NPP_URLNotifyProcPtr NPP_URLNotify; + NPP_GetValueProcPtr NPP_GetValue; + NPP_SetValueProcPtr NPP_SetValue; + NPP_ShutdownProcPtr NPP_Shutdown; + NPP_GetJavaClassProcPtr NPP_GetJavaClass; + + BOOL isLoaded; + BOOL needsUnload; + unsigned int instanceCount; + +#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; + +- (NPP_NewProcPtr)NPP_New; +- (NPP_DestroyProcPtr)NPP_Destroy; +- (NPP_SetWindowProcPtr)NPP_SetWindow; +- (NPP_NewStreamProcPtr)NPP_NewStream; +- (NPP_WriteReadyProcPtr)NPP_WriteReady; +- (NPP_WriteProcPtr)NPP_Write; +- (NPP_StreamAsFileProcPtr)NPP_StreamAsFile; +- (NPP_DestroyStreamProcPtr)NPP_DestroyStream; +- (NPP_HandleEventProcPtr)NPP_HandleEvent; +- (NPP_URLNotifyProcPtr)NPP_URLNotify; +- (NPP_GetValueProcPtr)NPP_GetValue; +- (NPP_SetValueProcPtr)NPP_SetValue; +- (NPP_PrintProcPtr)NPP_Print; +- (NPPluginFuncs *)pluginFuncs; + +@end +#endif diff --git a/WebKit/mac/Plugins/WebNetscapePluginPackage.m b/WebKit/mac/Plugins/WebNetscapePluginPackage.m new file mode 100644 index 0000000..0767c1d --- /dev/null +++ b/WebKit/mac/Plugins/WebNetscapePluginPackage.m @@ -0,0 +1,798 @@ +/* + * 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 "WebKitLogging.h" +#import "WebKitNSStringExtras.h" +#import "WebNSObjectExtras.h" +#import "WebNetscapeDeprecatedFunctions.h" +#import <WebCore/npruntime_impl.h> + +#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 *)[path fileSystemRepresentation], &fref, NULL); + if (err != noErr) + return -1; + + return FSOpenResFile(&fref, fsRdPerm); + } +#endif + + return CFBundleOpenBundleResourceMap(cfBundle); +} + +- (void)closeResourceFile:(ResFileRefNum)resRef +{ +#ifdef SUPPORT_CFM + if (!isBundle) { + CloseResFile(resRef); + return; + } +#endif + + CFBundleCloseBundleResourceMap(cfBundle, 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; + + NSMutableDictionary *MIMEToExtensionsDictionary = [NSMutableDictionary dictionary]; + NSMutableDictionary *MIMEToDescriptionDictionary = [NSMutableDictionary dictionary]; + + for (i=1; 1; i+=2) { + MIME = [[self stringForStringListID:MIMEListStringStringNumber + andIndex:i] lowercaseString]; + if (!MIME) + break; + + extensionsList = [[self stringForStringListID:MIMEListStringStringNumber andIndex:i+1] lowercaseString]; + if (extensionsList) { + extensions = [extensionsList componentsSeparatedByString:@","]; + [MIMEToExtensionsDictionary setObject:extensions forKey:MIME]; + } else + // DRM and WMP claim MIMEs without extensions. Use a @"" extension in this case. + [MIMEToExtensionsDictionary setObject:[NSArray arrayWithObject:@""] forKey:MIME]; + + description = [self stringForStringListID:MIMEDescriptionStringNumber + andIndex:[MIMEToExtensionsDictionary count]]; + if (description) + [MIMEToDescriptionDictionary setObject:description forKey:MIME]; + else + [MIMEToDescriptionDictionary setObject:@"" forKey:MIME]; + } + + [self setMIMEToDescriptionDictionary:MIMEToDescriptionDictionary]; + [self setMIMEToExtensionsDictionary:MIMEToExtensionsDictionary]; + + NSString *filename = [self filename]; + + description = [self stringForStringListID:PluginNameOrDescriptionStringNumber andIndex:1]; + if (!description) + description = filename; + [self setPluginDescription:description]; + + + NSString *theName = [self stringForStringListID:PluginNameOrDescriptionStringNumber andIndex:2]; + if (!theName) + theName = filename; + [self setName:theName]; + + [self closeResourceFile:resRef]; + + return YES; +} + +- (BOOL)_initWithPath:(NSString *)pluginPath +{ + resourceRef = -1; + + OSType type = 0; + + if (bundle) { + // Bundle + CFBundleGetPackageInfo(cfBundle, &type, NULL); +#ifdef SUPPORT_CFM + isBundle = YES; +#endif + } else { +#ifdef SUPPORT_CFM + // Single-file plug-in with resource fork + type = [[[NSFileManager defaultManager] fileAttributesAtPath:path traverseLink:YES] 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 (bundle) { + NSFileHandle *executableFile = [NSFileHandle fileHandleForReadingAtPath:[bundle executablePath]]; + 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 (![self isNativeLibraryData:data]) + return NO; + } + + 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; +} + +- (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 ([(NSString *)CFBundleGetIdentifier(cfBundle) isEqualToString:@"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)load +{ + 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, [self name]); + + if (isLoaded) + return YES; + +#ifdef SUPPORT_CFM + if (isBundle) { +#endif + if (!CFBundleLoadExecutable(cfBundle)) + goto abort; +#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, CFSTR("main") ); + if (!pluginMainFunc) + goto abort; + } else { +#endif + NP_Initialize = (NP_InitializeFuncPtr)CFBundleGetFunctionPointerForName(cfBundle, CFSTR("NP_Initialize")); + NP_GetEntryPoints = (NP_GetEntryPointsFuncPtr)CFBundleGetFunctionPointerForName(cfBundle, CFSTR("NP_GetEntryPoints")); + NPP_Shutdown = (NPP_ShutdownProcPtr)CFBundleGetFunctionPointerForName(cfBundle, CFSTR("NP_Shutdown")); + if (!NP_Initialize || !NP_GetEntryPoints || !NPP_Shutdown) + goto abort; +#ifdef SUPPORT_CFM + } + } else { + // single CFM file + FSSpec spec; + FSRef fref; + OSErr err; + + err = FSPathMakeRef((UInt8 *)[path fileSystemRepresentation], &fref, NULL); + if (err != noErr) { + LOG_ERROR("FSPathMakeRef failed. Error=%d", err); + goto abort; + } + err = FSGetCatalogInfo(&fref, kFSCatInfoNone, NULL, NULL, &spec, NULL); + if (err != noErr) { + LOG_ERROR("FSGetCatalogInfo failed. Error=%d", err); + goto abort; + } + err = WebGetDiskFragment(&spec, 0, kCFragGoesToEOF, nil, kPrivateCFragCopy, &connID, (Ptr *)&pluginMainFunc, nil); + if (err != noErr) { + LOG_ERROR("WebGetDiskFragment failed. Error=%d", err); + goto abort; + } +#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) { + goto abort; + } + + // 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.scheduletimer = (NPN_ScheduleTimerProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_ScheduleTimer); + browserFuncs.unscheduletimer = (NPN_UnscheduleTimerProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_UnscheduleTimer); + browserFuncs.popupcontextmenu = (NPN_PopUpContextMenuProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_PopUpContextMenu); + + 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.invoke = (NPN_InvokeProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_Invoke); + browserFuncs.invokeDefault = (NPN_InvokeDefaultProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_InvokeDefault); + browserFuncs.evaluate = (NPN_EvaluateProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_Evaluate); + 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); + NPP_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(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 && [[self filename] isEqualToString:RealPlayerPluginFilename]) + [self launchRealPlayer]; + goto abort; + } +#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); + + NPP_New = (NPP_NewProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.newp); + NPP_Destroy = (NPP_DestroyProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.destroy); + NPP_SetWindow = (NPP_SetWindowProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.setwindow); + NPP_NewStream = (NPP_NewStreamProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.newstream); + NPP_DestroyStream = (NPP_DestroyStreamProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.destroystream); + NPP_StreamAsFile = (NPP_StreamAsFileProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.asfile); + NPP_WriteReady = (NPP_WriteReadyProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.writeready); + NPP_Write = (NPP_WriteProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.write); + NPP_Print = (NPP_PrintProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.print); + NPP_HandleEvent = (NPP_HandleEventProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.event); + NPP_URLNotify = (NPP_URLNotifyProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.urlnotify); + NPP_GetValue = (NPP_GetValueProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.getvalue); + NPP_SetValue = (NPP_SetValueProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.setvalue); + + // LiveConnect support + NPP_GetJavaClass = (NPP_GetJavaClassProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.javaClass); + if (NPP_GetJavaClass) { + LOG(LiveConnect, "%@: CFM entry point for NPP_GetJavaClass = %p", [self name], NPP_GetJavaClass); + } else { + LOG(LiveConnect, "%@: no entry point for NPP_GetJavaClass", [self 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.scheduletimer = NPN_ScheduleTimer; + browserFuncs.unscheduletimer = NPN_UnscheduleTimer; + browserFuncs.popupcontextmenu = NPN_PopUpContextMenu; + + 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.invoke = _NPN_Invoke; + browserFuncs.invokeDefault = _NPN_InvokeDefault; + browserFuncs.evaluate = _NPN_Evaluate; + 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) + goto abort; +#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) + goto abort; + + pluginSize = pluginFuncs.size; + pluginVersion = pluginFuncs.version; + + NPP_New = pluginFuncs.newp; + NPP_Destroy = pluginFuncs.destroy; + NPP_SetWindow = pluginFuncs.setwindow; + NPP_NewStream = pluginFuncs.newstream; + NPP_DestroyStream = pluginFuncs.destroystream; + NPP_StreamAsFile = pluginFuncs.asfile; + NPP_WriteReady = pluginFuncs.writeready; + NPP_Write = pluginFuncs.write; + NPP_Print = pluginFuncs.print; + NPP_HandleEvent = pluginFuncs.event; + NPP_URLNotify = pluginFuncs.urlnotify; + NPP_GetValue = pluginFuncs.getvalue; + NPP_SetValue = pluginFuncs.setvalue; + + // LiveConnect support + NPP_GetJavaClass = pluginFuncs.javaClass; + if (NPP_GetJavaClass){ + LOG(LiveConnect, "%@: mach-o entry point for NPP_GetJavaClass = %p", [self name], NPP_GetJavaClass); + } else { + LOG(LiveConnect, "%@: no entry point for NPP_GetJavaClass", [self 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 [super load]; + +abort: + [self _unloadWithShutdown:NO]; + return NO; +} + +- (NPP_SetWindowProcPtr)NPP_SetWindow +{ + return NPP_SetWindow; +} + +- (NPP_NewProcPtr)NPP_New +{ + return NPP_New; +} + +- (NPP_DestroyProcPtr)NPP_Destroy +{ + return NPP_Destroy; +} + +- (NPP_NewStreamProcPtr)NPP_NewStream +{ + return NPP_NewStream; +} + +- (NPP_StreamAsFileProcPtr)NPP_StreamAsFile +{ + return NPP_StreamAsFile; +} +- (NPP_DestroyStreamProcPtr)NPP_DestroyStream +{ + return NPP_DestroyStream; +} + +- (NPP_WriteReadyProcPtr)NPP_WriteReady +{ + return NPP_WriteReady; +} +- (NPP_WriteProcPtr)NPP_Write +{ + return NPP_Write; +} + +- (NPP_HandleEventProcPtr)NPP_HandleEvent +{ + return NPP_HandleEvent; +} + +-(NPP_URLNotifyProcPtr)NPP_URLNotify +{ + return NPP_URLNotify; +} + +-(NPP_GetValueProcPtr)NPP_GetValue +{ + return NPP_GetValue; +} + +-(NPP_SetValueProcPtr)NPP_SetValue +{ + return NPP_SetValue; +} + +-(NPP_PrintProcPtr)NPP_Print +{ + return NPP_Print; +} + +- (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]; +} + +@end + +#ifdef SUPPORT_CFM + +// function pointer converters + +FunctionPointer functionPointerForTVector(TransitionVector tvp) +{ + const uint32 temp[6] = {0x3D800000, 0x618C0000, 0x800C0000, 0x804C0004, 0x7C0903A6, 0x4E800420}; + uint32 *newGlue = NULL; + + if (tvp != NULL) { + newGlue = (uint32 *)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 %@...", name); + + // Cannot unload a plug-in package while an instance is still using it + if (instanceCount > 0) { + needsUnload = YES; + return; + } + + if (shutdown && NPP_Shutdown) + NPP_Shutdown(); + + if (resourceRef != -1) + [self closeResourceFile:resourceRef]; + +#ifdef SUPPORT_CFM + if (isBundle) +#endif + CFBundleUnloadExecutable(cfBundle); +#ifdef SUPPORT_CFM + else + WebCloseConnection(&connID); +#endif + + LOG(Plugins, "Plugin Unloaded"); + isLoaded = NO; +} + +@end +#endif diff --git a/WebKit/mac/Plugins/WebNullPluginView.h b/WebKit/mac/Plugins/WebNullPluginView.h new file mode 100644 index 0000000..3ca1532 --- /dev/null +++ b/WebKit/mac/Plugins/WebNullPluginView.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#import <AppKit/AppKit.h> + +@class DOMElement; + +@interface WebNullPluginView : NSImageView +{ + NSError *error; + DOMElement *element; +} + +- (id)initWithFrame:(NSRect)frame error:(NSError *)error DOMElement:(DOMElement *)element; + +@end diff --git a/WebKit/mac/Plugins/WebNullPluginView.mm b/WebKit/mac/Plugins/WebNullPluginView.mm new file mode 100644 index 0000000..48e0e9d --- /dev/null +++ b/WebKit/mac/Plugins/WebNullPluginView.mm @@ -0,0 +1,97 @@ +/* + * 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. + */ + +#import "WebNullPluginView.h" + +#import "WebFrameInternal.h" +#import "WebViewInternal.h" +#import <WebCore/Document.h> + +@implementation WebNullPluginView + +- initWithFrame:(NSRect)frame error:(NSError *)err DOMElement:(DOMElement *)elem +{ + static NSImage *nullPlugInImage; + if (!nullPlugInImage) { + NSBundle *bundle = [NSBundle bundleForClass:[WebNullPluginView class]]; + NSString *imagePath = [bundle pathForResource:@"nullplugin" ofType:@"tiff"]; + nullPlugInImage = [[NSImage alloc] initWithContentsOfFile:imagePath]; + } + + self = [super initWithFrame:frame]; + if (!self) + return nil; + + error = [err retain]; + if (err) + element = [elem retain]; + + [self setImage:nullPlugInImage]; + + return self; +} + +- (void)dealloc + +{ + [error release]; + [element release]; + [super dealloc]; +} + +- (void)reportFailure +{ + NSError *localError = error; + DOMElement *localElement = element; + + error = nil; + element = nil; + + WebFrame *webFrame = kit(core(localElement)->document()->frame()); + if (webFrame) { + WebView *webView = [webFrame webView]; + WebResourceDelegateImplementationCache* implementations = WebViewGetResourceLoadDelegateImplementations(webView); + if (implementations->plugInFailedWithErrorFunc) + CallResourceLoadDelegate(implementations->plugInFailedWithErrorFunc, webView, + @selector(webView:plugInFailedWithError:dataSource:), localError, [webFrame _dataSource]); + } + + [localError release]; + [localElement release]; +} + +- (void)viewDidMoveToWindow +{ + if (!error) + return; + + if ([self window]) + [self reportFailure]; +} + +@end diff --git a/WebKit/mac/Plugins/WebPlugInStreamLoaderDelegate.h b/WebKit/mac/Plugins/WebPlugInStreamLoaderDelegate.h new file mode 100644 index 0000000..c21fe4c --- /dev/null +++ b/WebKit/mac/Plugins/WebPlugInStreamLoaderDelegate.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +@class NSURLResponse; +@class NSError; +@class NSData; + +@protocol WebPlugInStreamLoaderDelegate + +- (void)startStreamWithResponse:(NSURLResponse *)r; + +// destroyStreamWithError tells the plug-in that the load is completed (error == nil) or ended in error. +- (void)destroyStreamWithError:(NSError *)error; + +// cancelLoadAndDestoryStreamWithError calls cancelLoadWithError: then destroyStreamWithError:. +- (void)cancelLoadAndDestroyStreamWithError:(NSError *)error; + +- (void)receivedData:(NSData *)data; +- (void)finishedLoading; + +- (BOOL)wantsAllStreams; + +@end diff --git a/WebKit/mac/Plugins/WebPlugin.h b/WebKit/mac/Plugins/WebPlugin.h new file mode 100644 index 0000000..bd0600e --- /dev/null +++ b/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_AFTER_WEBKIT_VERSION_3_1); + +/*! + @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_AFTER_WEBKIT_VERSION_3_1); + +/*! + @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_AFTER_WEBKIT_VERSION_3_1); + +/*! + @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_AFTER_WEBKIT_VERSION_3_1); + +@end diff --git a/WebKit/mac/Plugins/WebPluginContainer.h b/WebKit/mac/Plugins/WebPluginContainer.h new file mode 100644 index 0000000..112b677 --- /dev/null +++ b/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/WebKit/mac/Plugins/WebPluginContainerCheck.h b/WebKit/mac/Plugins/WebPluginContainerCheck.h new file mode 100644 index 0000000..1b7bbda --- /dev/null +++ b/WebKit/mac/Plugins/WebPluginContainerCheck.h @@ -0,0 +1,55 @@ +/* + * 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 WebPluginController; +@class WebPolicyDecisionListener; + +@interface WebPluginContainerCheck : NSObject +{ + NSURLRequest *_request; + NSString *_target; + WebPluginController *_controller; + id _resultObject; + SEL _resultSelector; + BOOL _done; + WebPolicyDecisionListener *_listener; +} + ++ (id)checkWithRequest:(NSURLRequest *)request target:(NSString *)target resultObject:(id)obj selector:(SEL)selector controller:(WebPluginController *)controller; + +- (id)initWithRequest:(NSURLRequest *)request target:(NSString *)target resultObject:(id)obj selector:(SEL)selector controller:(WebPluginController *)controller; + +- (void)start; + +- (void)cancel; + +@end diff --git a/WebKit/mac/Plugins/WebPluginContainerCheck.mm b/WebKit/mac/Plugins/WebPluginContainerCheck.mm new file mode 100644 index 0000000..5c9ecd7 --- /dev/null +++ b/WebKit/mac/Plugins/WebPluginContainerCheck.mm @@ -0,0 +1,180 @@ +/* + * 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 <wtf/Assertions.h> +#import <objc/objc-runtime.h> + +using namespace WebCore; + +@implementation WebPluginContainerCheck + ++ (id)checkWithRequest:(NSURLRequest *)request target:(NSString *)target resultObject:(id)obj selector:(SEL)selector controller:(WebPluginController *)controller +{ + return [[[self alloc] initWithRequest:request target:target resultObject:obj selector:selector controller:controller] autorelease]; +} + +- (id)initWithRequest:(NSURLRequest *)request target:(NSString *)target resultObject:(id)obj selector:(SEL)selector controller:(WebPluginController *)controller +{ + if (!(self = [super init])) + return nil; + + _request = [request copy]; + _target = [target copy]; + _resultObject = [obj retain]; + _resultSelector = selector; + + // controller owns us so don't retain, to avoid cycle + _controller = controller; + + return self; +} + +- (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 +{ + ((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->loader()->canLoad([_request URL], String(), coreFrame->document())) { + [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; + + _done = YES; +} + +@end diff --git a/WebKit/mac/Plugins/WebPluginContainerPrivate.h b/WebKit/mac/Plugins/WebPluginContainerPrivate.h new file mode 100644 index 0000000..c9d64d5 --- /dev/null +++ b/WebKit/mac/Plugins/WebPluginContainerPrivate.h @@ -0,0 +1,37 @@ +/* + * 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> + +@interface NSObject (WebPlugInContainerPrivate) + +- (id)_webPluginContainerCheckIfAllowedToLoadRequest:(NSURLRequest *)Request inFrame:(NSString *)target resultObject:(id)obj selector:(SEL)selector; + +- (void)_webPluginContainerCancelCheckIfAllowedToLoadRequest:(id)checkIdentifier; + +@end diff --git a/WebKit/mac/Plugins/WebPluginController.h b/WebKit/mac/Plugins/WebPluginController.h new file mode 100644 index 0000000..58217c3 --- /dev/null +++ b/WebKit/mac/Plugins/WebPluginController.h @@ -0,0 +1,65 @@ +/* + * 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> + +@class WebFrame; +@class WebHTMLView; +@class WebPluginPackage; +@class WebView; +@class WebDataSource; + +@interface WebPluginController : NSObject <WebPluginManualLoader> +{ + NSView *_documentView; + WebDataSource *_dataSource; + NSMutableArray *_views; + BOOL _started; + NSMutableSet *_checksInProgress; +} + ++ (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; + +- (void)startAllPlugins; +- (void)stopAllPlugins; +- (void)destroyAllPlugins; + +- (WebFrame *)webFrame; +- (WebView *)webView; + +- (NSString *)URLPolicyCheckReferrer; + +@end diff --git a/WebKit/mac/Plugins/WebPluginController.mm b/WebKit/mac/Plugins/WebPluginController.mm new file mode 100644 index 0000000..d892d4a --- /dev/null +++ b/WebKit/mac/Plugins/WebPluginController.mm @@ -0,0 +1,433 @@ +/* + * 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 <WebKit/WebPluginController.h> + +#import <Foundation/NSURLRequest.h> +#import <runtime/JSLock.h> +#import <WebCore/Frame.h> +#import <WebCore/FrameLoader.h> +#import <WebCore/ResourceRequest.h> +#import <WebCore/PlatformString.h> +#import <WebCore/DocumentLoader.h> +#import <WebCore/ScriptController.h> +#import <WebKit/WebDataSourceInternal.h> +#import <WebKit/WebFrameInternal.h> +#import <WebKit/WebFrameView.h> +#import <WebKit/WebHTMLViewPrivate.h> +#import <WebKit/WebKitErrorsPrivate.h> +#import <WebKit/WebKitLogging.h> +#import <WebKit/WebNSURLExtras.h> +#import <WebKit/WebNSViewExtras.h> +#import <WebKit/WebPlugin.h> +#import <WebKit/WebPluginContainer.h> +#import <WebKit/WebPluginContainerCheck.h> +#import <WebKit/WebPluginPackage.h> +#import <WebKit/WebPluginPrivate.h> +#import <WebKit/WebPluginViewFactory.h> +#import <WebKit/WebUIDelegate.h> +#import <WebKit/WebViewInternal.h> + +using namespace WebCore; + +@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 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(false); + view = [viewFactory plugInViewWithArguments:arguments]; + } else if ([viewFactory respondsToSelector:@selector(pluginViewWithArguments:)]) { + JSC::JSLock::DropAllLocks dropAllLocks(false); + 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]; + [super dealloc]; +} + +- (void)startAllPlugins +{ + if (_started) + return; + + if ([_views count] > 0) + LOG(Plugins, "starting WebKit plugins : %@", [_views description]); + + int i, count = [_views count]; + for (i = 0; i < count; i++) { + id aView = [_views objectAtIndex:i]; + if ([aView respondsToSelector:@selector(webPlugInStart)]) { + JSC::JSLock::DropAllLocks dropAllLocks(false); + [aView webPlugInStart]; + } else if ([aView respondsToSelector:@selector(pluginStart)]) { + JSC::JSLock::DropAllLocks dropAllLocks(false); + [aView pluginStart]; + } + } + _started = YES; +} + +- (void)stopAllPlugins +{ + if (!_started) + return; + + if ([_views count] > 0) { + LOG(Plugins, "stopping WebKit plugins: %@", [_views description]); + } + + int i, count = [_views count]; + for (i = 0; i < count; i++) { + id aView = [_views objectAtIndex:i]; + if ([aView respondsToSelector:@selector(webPlugInStop)]) { + JSC::JSLock::DropAllLocks dropAllLocks(false); + [aView webPlugInStop]; + } else if ([aView respondsToSelector:@selector(pluginStop)]) { + JSC::JSLock::DropAllLocks dropAllLocks(false); + [aView pluginStop]; + } + } + _started = NO; +} + +- (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]; + + LOG(Plugins, "initializing plug-in %@", view); + if ([view respondsToSelector:@selector(webPlugInInitialize)]) { + JSC::JSLock::DropAllLocks dropAllLocks(false); + [view webPlugInInitialize]; + } else if ([view respondsToSelector:@selector(pluginInitialize)]) { + JSC::JSLock::DropAllLocks dropAllLocks(false); + [view pluginInitialize]; + } + + if (_started) { + LOG(Plugins, "starting plug-in %@", view); + if ([view respondsToSelector:@selector(webPlugInStart)]) { + JSC::JSLock::DropAllLocks dropAllLocks(false); + [view webPlugInStart]; + } else if ([view respondsToSelector:@selector(pluginStart)]) { + JSC::JSLock::DropAllLocks dropAllLocks(false); + [view pluginStart]; + } + + if ([view respondsToSelector:@selector(setContainingWindow:)]) { + JSC::JSLock::DropAllLocks dropAllLocks(false); + [view setContainingWindow:[_documentView window]]; + } + } + } +} + +- (void)destroyPlugin:(NSView *)view +{ + if ([_views containsObject:view]) { + if (_started) { + if ([view respondsToSelector:@selector(webPlugInStop)]) { + JSC::JSLock::DropAllLocks dropAllLocks(false); + [view webPlugInStop]; + } else if ([view respondsToSelector:@selector(pluginStop)]) { + JSC::JSLock::DropAllLocks dropAllLocks(false); + [view pluginStop]; + } + } + + if ([view respondsToSelector:@selector(webPlugInDestroy)]) { + JSC::JSLock::DropAllLocks dropAllLocks(false); + [view webPlugInDestroy]; + } else if ([view respondsToSelector:@selector(pluginDestroy)]) { + JSC::JSLock::DropAllLocks dropAllLocks(false); + [view pluginDestroy]; + } + + if (Frame* frame = core([self webFrame])) + frame->script()->cleanupScriptObjectsForPlugin(self); + + [pluginViews removeObject:view]; + [[_documentView _webView] removePluginInstanceView:view]; + [_views removeObject:view]; + } +} + +- (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 i, count = [_views count]; + for (i = 0; i < count; i++) { + id aView = [_views objectAtIndex:i]; + if ([aView respondsToSelector:@selector(webPlugInDestroy)]) { + JSC::JSLock::DropAllLocks dropAllLocks(false); + [aView webPlugInDestroy]; + } else if ([aView respondsToSelector:@selector(pluginDestroy)]) { + JSC::JSLock::DropAllLocks dropAllLocks(false); + [aView pluginDestroy]; + } + + if (Frame* frame = core([self webFrame])) + frame->script()->cleanupScriptObjectsForPlugin(self); + + [pluginViews removeObject:aView]; + [[_documentView _webView] removePluginInstanceView:aView]; + } + [_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]; + [_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); + } +} + +// For compatibility only. +- (void)showURL:(NSURL *)URL inFrame:(NSString *)target +{ + [self webPlugInContainerLoadRequest:[NSURLRequest requestWithURL:URL] inFrame:target]; +} + +- (void)webPlugInContainerShowStatus:(NSString *)message +{ + if (!message) { + message = @""; + } + if (!_documentView) { + LOG_ERROR("could not show status message (%@) because plug-in has already been destroyed", message); + return; + } + 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]; +} + +@end diff --git a/WebKit/mac/Plugins/WebPluginDatabase.h b/WebKit/mac/Plugins/WebPluginDatabase.h new file mode 100644 index 0000000..350f0a7 --- /dev/null +++ b/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/WebKit/mac/Plugins/WebPluginDatabase.mm b/WebKit/mac/Plugins/WebPluginDatabase.mm new file mode 100644 index 0000000..531214e --- /dev/null +++ b/WebKit/mac/Plugins/WebPluginDatabase.mm @@ -0,0 +1,456 @@ +/* + * 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 "WebBasePluginPackage.h" +#import "WebDataSourcePrivate.h" +#import "WebFrame.h" +#import "WebFrameViewInternal.h" +#import "WebHTMLRepresentation.h" +#import "WebHTMLView.h" +#import "WebKitLogging.h" +#import "WebNetscapePluginPackage.h" +#import "WebNSFileManagerExtras.h" +#import "WebPluginController.h" +#import "WebBaseNetscapePluginView.h" +#import "WebPluginPackage.h" +#import "WebViewPrivate.h" +#import "WebHTMLView.h" +#import <WebKitSystemInterface.h> +#import <wtf/Assertions.h> + +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 bundle] bundleIdentifier] isEqualToString:[[*candidatePlugin bundle] bundleIdentifier]] && [*candidatePlugin versionNumber] > [*currentPlugin versionNumber]) + *currentPlugin = *candidatePlugin; +} + +- (WebBasePluginPackage *)pluginForKey:(NSString *)key withEnumeratorSelector:(SEL)enumeratorSelector +{ + WebBasePluginPackage *plugin = nil; + WebBasePluginPackage *webPlugin = nil; +#ifdef SUPPORT_CFM + WebBasePluginPackage *CFMPlugin = nil; +#endif + WebBasePluginPackage *machoPlugin = nil; + + NSEnumerator *pluginEnumerator = [plugins objectEnumerator]; + key = [key lowercaseString]; + + while ((plugin = [pluginEnumerator nextObject]) != nil) { + if ([[[plugin performSelector:enumeratorSelector] allObjects] containsObject:key]) { + if ([plugin isKindOfClass:[WebPluginPackage class]]) + checkCandidate(&webPlugin, &plugin); +#if ENABLE(NETSCAPE_PLUGIN_API) + else if([plugin isKindOfClass:[WebNetscapePluginPackage class]]) { + WebExecutableType executableType = [(WebNetscapePluginPackage *)plugin executableType]; +#ifdef SUPPORT_CFM + if (executableType == WebCFMExecutableType) { + checkCandidate(&CFMPlugin, &plugin); + } else +#endif // SUPPORT_CFM + if (executableType == WebMachOExecutableType) { + checkCandidate(&machoPlugin, &plugin); + } else { + ASSERT_NOT_REACHED(); + } + } else { + ASSERT_NOT_REACHED(); + } +#endif + } + } + + // 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; + + else if (machoPlugin && ![machoPlugin isQuickTimePlugIn]) + return machoPlugin; +#ifdef SUPPORT_CFM + else if (CFMPlugin && ![CFMPlugin isQuickTimePlugIn]) + return CFMPlugin; +#endif // SUPPORT_CFM + else if (webPlugin) + return webPlugin; + else if (machoPlugin) + return machoPlugin; +#ifdef SUPPORT_CFM + else if (CFMPlugin) + return CFMPlugin; +#endif + return nil; +} + +- (WebBasePluginPackage *)pluginForMIMEType:(NSString *)MIMEType +{ + return [self pluginForKey:[MIMEType lowercaseString] + withEnumeratorSelector:@selector(MIMETypeEnumerator)]; +} + +- (WebBasePluginPackage *)pluginForExtension:(NSString *)extension +{ + WebBasePluginPackage *plugin = [self pluginForKey:[extension lowercaseString] + withEnumeratorSelector:@selector(extensionEnumerator)]; + 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]) != nil) + [MIMETypes addObjectsFromArray:[[plugin MIMETypeEnumerator] allObjects]]; + + // 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 registerViewClass:[WebHTMLView class] representationClass:[WebHTMLRepresentation class] forMIMEType: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 ([subview isKindOfClass:[WebBaseNetscapePluginView class]] || [WebPluginController isPlugInView:subview]) + [pluginInstanceViews removeObject:subview]; + } + } +} + +- (void)destroyAllPluginInstanceViews +{ + NSView *view; + NSArray *pli = [pluginInstanceViews allObjects]; + NSEnumerator *enumerator = [pli objectEnumerator]; + while ((view = [enumerator nextObject]) != nil) { + if ([view isKindOfClass:[WebBaseNetscapePluginView class]]) { + ASSERT([view respondsToSelector:@selector(stop)]); + [view performSelector:@selector(stop)]; + } else 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 + NSEnumerator *MIMETypeEnumerator = [plugin MIMETypeEnumerator]; + NSString *MIMEType; + while ((MIMEType = [MIMETypeEnumerator nextObject])) { + if ([registeredMIMETypes containsObject:MIMEType]) { + if (self == sharedDatabase) + [WebView _unregisterViewClassAndRepresentationClassForMIMEType: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/WebKit/mac/Plugins/WebPluginPackage.h b/WebKit/mac/Plugins/WebPluginPackage.h new file mode 100644 index 0000000..290bb1b --- /dev/null +++ b/WebKit/mac/Plugins/WebPluginPackage.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 <Foundation/Foundation.h> + +#import <WebKit/WebBasePluginPackage.h> + +@protocol WebPluginViewFactory; + +@interface WebPluginPackage : WebBasePluginPackage + +- (Class)viewFactory; + +@end diff --git a/WebKit/mac/Plugins/WebPluginPackage.m b/WebKit/mac/Plugins/WebPluginPackage.m new file mode 100644 index 0000000..d7f144e --- /dev/null +++ b/WebKit/mac/Plugins/WebPluginPackage.m @@ -0,0 +1,116 @@ +/* + * 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 + +- initWithPath:(NSString *)pluginPath +{ + if (!(self = [super initWithPath:pluginPath])) + return nil; + + if (bundle == nil) { + [self release]; + return nil; + } + + if (![[pluginPath pathExtension] _webkit_isCaseInsensitiveEqualToString:@"webplugin"]) { + UInt32 type = 0; + CFBundleGetPackageInfo(cfBundle, &type, NULL); + if (type != FOUR_CHAR_CODE('WBPL')) { + [self release]; + return nil; + } + } + + NSFileHandle *executableFile = [NSFileHandle fileHandleForReadingAtPath:[bundle 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; +} + +- (Class)viewFactory +{ + return [bundle principalClass]; +} + +- (BOOL)load +{ +#if !LOG_DISABLED + CFAbsoluteTime start = CFAbsoluteTimeGetCurrent(); +#endif + + // Load the bundle + if (![bundle isLoaded]) { + if (![bundle load]) + return NO; + } + +#if !LOG_DISABLED + CFAbsoluteTime duration = CFAbsoluteTimeGetCurrent() - start; + LOG(Plugins, "principalClass took %f seconds for: %@", duration, [self 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/WebKit/mac/Plugins/WebPluginPrivate.h b/WebKit/mac/Plugins/WebPluginPrivate.h new file mode 100644 index 0000000..56e4a3a --- /dev/null +++ b/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/WebKit/mac/Plugins/WebPluginViewFactory.h b/WebKit/mac/Plugins/WebPluginViewFactory.h new file mode 100644 index 0000000..583a73d --- /dev/null +++ b/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_AFTER_WEBKIT_VERSION_3_1; + +/*! + @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/WebKit/mac/Plugins/WebPluginViewFactoryPrivate.h b/WebKit/mac/Plugins/WebPluginViewFactoryPrivate.h new file mode 100644 index 0000000..51d4283 --- /dev/null +++ b/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/WebKit/mac/Plugins/WebPluginsPrivate.h b/WebKit/mac/Plugins/WebPluginsPrivate.h new file mode 100644 index 0000000..fc0067d --- /dev/null +++ b/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/WebKit/mac/Plugins/WebPluginsPrivate.m b/WebKit/mac/Plugins/WebPluginsPrivate.m new file mode 100644 index 0000000..79896b6 --- /dev/null +++ b/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/WebKit/mac/Plugins/npapi.mm b/WebKit/mac/Plugins/npapi.mm new file mode 100644 index 0000000..f85ec9f --- /dev/null +++ b/WebKit/mac/Plugins/npapi.mm @@ -0,0 +1,209 @@ +/* + * 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 <WebKit/nptextinput.h> + +#import "WebBaseNetscapePluginView.h" +#import "WebKitLogging.h" +#import <WebCore/PluginMainThreadScheduler.h> + +using namespace WebCore; + +WebBaseNetscapePluginView *pluginViewForInstance(NPP instance); + +// general plug-in to browser functions + +void* NPN_MemAlloc(uint32 size) +{ + return malloc(size); +} + +void NPN_MemFree(void* ptr) +{ + free(ptr); +} + +uint32 NPN_MemFlush(uint32 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 [WebBaseNetscapePluginView start]. +// This specifically works around Flash and Shockwave. When we call NPP_New, they call NPN_UserAgent with a NULL instance. +WebBaseNetscapePluginView *pluginViewForInstance(NPP instance) +{ + if (instance && instance->ndata) + return (WebBaseNetscapePluginView *)instance->ndata; + else + return [WebBaseNetscapePluginView 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 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 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 NPN_Write(NPP instance, NPStream* stream, int32 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 NPN_ScheduleTimer(NPP instance, uint32 interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32 timerID)) +{ + return [pluginViewForInstance(instance) scheduleTimerWithInterval:interval repeat:repeat timerFunc:timerFunc]; +} + +void NPN_UnscheduleTimer(NPP instance, uint32 timerID) +{ + [pluginViewForInstance(instance) unscheduleTimer:timerID]; +} + +NPError NPN_PopUpContextMenu(NPP instance, NPMenu *menu) +{ + return [pluginViewForInstance(instance) popUpContextMenu:menu]; +} + +void NPN_MarkedTextAbandoned(NPP instance) +{ + WebBaseNetscapePluginView *pluginView = pluginViewForInstance(instance); + + [[NSInputManager currentInputManager] markedTextAbandoned:pluginView]; +} + +void NPN_MarkedTextSelectionChanged(NPP instance, NSRange newSel) +{ + WebBaseNetscapePluginView *pluginView = pluginViewForInstance(instance); + + [[NSInputManager currentInputManager] markedTextSelectionChanged:newSel client:pluginView]; +} + +#endif diff --git a/WebKit/mac/Plugins/nptextinput.h b/WebKit/mac/Plugins/nptextinput.h new file mode 100644 index 0000000..2ed3146 --- /dev/null +++ b/WebKit/mac/Plugins/nptextinput.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2008, Apple Inc. and The Mozilla Foundation. + * 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 names of Apple Inc. ("Apple") or The Mozilla + * Foundation ("Mozilla") nor the names of their contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE, MOZILLA AND THEIR 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, MOZILLA OR + * THEIR 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 __OBJC__ +#error "npinput.h can only be included from Objective-C code." +#endif + +#ifndef _NP_TEXTINPUT_H_ +#define _NP_TEXTINPUT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <WebKit/npapi.h> + +#import <Cocoa/Cocoa.h> + +typedef void (*NPP_InsertTextFunc)(NPP npp, id aString); +typedef void (*NPP_DoCommandBySelectorFunc)(NPP npp, SEL aSelector); +typedef void (*NPP_SetMarkedTextFunc)(NPP npp, id aString, NSRange selRange); +typedef void (*NPP_UnmarkTextFunc)(NPP npp); +typedef BOOL (*NPP_HasMarkedTextFunc)(NPP npp); +typedef NSAttributedString * (*NPP_AttributedSubstringFromRangeFunc)(NPP npp, NSRange theRange); +typedef NSRange (*NPP_MarkedRangeFunc)(NPP npp); +typedef NSRange (*NPP_SelectedRangeFunc)(NPP npp); +typedef NSRect (*NPP_FirstRectForCharacterRangeFunc)(NPP npp, NSRange theRange); +typedef unsigned long long (*NPP_CharacterIndexForPointFunc)(NPP npp, NSPoint thePoint); +typedef NSArray *(*NPP_ValidAttributesForMarkedTextFunc)(NPP npp); + +typedef struct _NPPluginTextInputFuncs { + uint16 size; + uint16 version; + + NPP_InsertTextFunc insertText; + NPP_DoCommandBySelectorFunc doCommandBySelector; + NPP_SetMarkedTextFunc setMarkedText; + NPP_UnmarkTextFunc unmarkText; + NPP_HasMarkedTextFunc hasMarkedText; + NPP_AttributedSubstringFromRangeFunc attributedSubstringFromRange; + NPP_MarkedRangeFunc markedRange; + NPP_SelectedRangeFunc selectedRange; + NPP_FirstRectForCharacterRangeFunc firstRectForCharacterRange; + NPP_CharacterIndexForPointFunc characterIndexForPoint; + NPP_ValidAttributesForMarkedTextFunc validAttributesForMarkedText; +} NPPluginTextInputFuncs; + +void NPP_InsertText(NPP npp, id aString); +void NPP_DoCommandBySelector(NPP npp, SEL aSelector); +void NPP_SetMarkedText(NPP npp, id aString, NSRange selRange); +void NPP_UnmarkText(NPP npp); +BOOL NPP_HasMarkedText(NPP npp); +NSAttributedString *NPP_AttributedSubstringFromRange(NPP npp, NSRange theRange); +NSRange NPP_MarkedRange(NPP npp); +NSRange NPP_SelectedRange(NPP npp); +NSRect NPP_FirstRectForCharacterRange(NPP npp, NSRange theRange); +unsigned long long NPP_CharacterIndexForPoint(NPP npp, NSPoint thePoint); +NSArray *NPP_ValidAttributesForMarkedText(NPP npp); + +typedef void (*NPN_MarkedTextAbandonedFunc)(NPP npp); +typedef void (*NPN_MarkedTextSelectionChangedFunc)(NPP npp, NSRange newSel); + +typedef struct _NPBrowserTextInputFuncs { + uint16 size; + uint16 version; + + NPN_MarkedTextAbandonedFunc markedTextAbandoned; + NPN_MarkedTextSelectionChangedFunc markedTextSelectionChanged; +} NPBrowserTextInputFuncs; + +void NPN_MarkedTextAbandoned(NPP npp); +void NPN_MarkedTextSelectionChanged(NPP npp, NSRange newSel); + +#ifdef __cplusplus +} +#endif + +#endif |