diff options
Diffstat (limited to 'Source/WebKit/mac/Plugins/Hosted/NetscapePluginInstanceProxy.mm')
| -rw-r--r-- | Source/WebKit/mac/Plugins/Hosted/NetscapePluginInstanceProxy.mm | 1684 |
1 files changed, 1684 insertions, 0 deletions
diff --git a/Source/WebKit/mac/Plugins/Hosted/NetscapePluginInstanceProxy.mm b/Source/WebKit/mac/Plugins/Hosted/NetscapePluginInstanceProxy.mm new file mode 100644 index 0000000..63b0899 --- /dev/null +++ b/Source/WebKit/mac/Plugins/Hosted/NetscapePluginInstanceProxy.mm @@ -0,0 +1,1684 @@ +/* + * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API) + +#import "NetscapePluginInstanceProxy.h" + +#import "HostedNetscapePluginStream.h" +#import "NetscapePluginHostProxy.h" +#import "ProxyInstance.h" +#import "ProxyRuntimeObject.h" +#import "WebDataSourceInternal.h" +#import "WebFrameInternal.h" +#import "WebHostedNetscapePluginView.h" +#import "WebKitNSStringExtras.h" +#import "WebNSDataExtras.h" +#import "WebNSURLExtras.h" +#import "WebPluginRequest.h" +#import "WebUIDelegate.h" +#import "WebUIDelegatePrivate.h" +#import "WebViewInternal.h" +#import <JavaScriptCore/Error.h> +#import <JavaScriptCore/JSLock.h> +#import <JavaScriptCore/PropertyNameArray.h> +#import <WebCore/CookieJar.h> +#import <WebCore/DocumentLoader.h> +#import <WebCore/Frame.h> +#import <WebCore/FrameLoader.h> +#import <WebCore/FrameTree.h> +#import <WebCore/KURL.h> +#import <WebCore/ProxyServer.h> +#import <WebCore/SecurityOrigin.h> +#import <WebCore/ScriptController.h> +#import <WebCore/ScriptValue.h> +#import <WebCore/StringSourceProvider.h> +#import <WebCore/npruntime_impl.h> +#import <WebCore/runtime_object.h> +#import <WebKitSystemInterface.h> +#import <mach/mach.h> +#import <utility> +#import <wtf/RefCountedLeakCounter.h> +#import <wtf/text/CString.h> + +extern "C" { +#import "WebKitPluginClientServer.h" +#import "WebKitPluginHost.h" +} + +using namespace JSC; +using namespace JSC::Bindings; +using namespace std; +using namespace WebCore; + +namespace WebKit { + +class NetscapePluginInstanceProxy::PluginRequest : public RefCounted<NetscapePluginInstanceProxy::PluginRequest> { +public: + static PassRefPtr<PluginRequest> create(uint32_t requestID, NSURLRequest* request, NSString* frameName, bool allowPopups) + { + return adoptRef(new PluginRequest(requestID, request, frameName, allowPopups)); + } + + uint32_t requestID() const { return m_requestID; } + NSURLRequest* request() const { return m_request.get(); } + NSString* frameName() const { return m_frameName.get(); } + bool allowPopups() const { return m_allowPopups; } + +private: + PluginRequest(uint32_t requestID, NSURLRequest* request, NSString* frameName, bool allowPopups) + : m_requestID(requestID) + , m_request(request) + , m_frameName(frameName) + , m_allowPopups(allowPopups) + { + } + + uint32_t m_requestID; + RetainPtr<NSURLRequest*> m_request; + RetainPtr<NSString*> m_frameName; + bool m_allowPopups; +}; + +NetscapePluginInstanceProxy::LocalObjectMap::LocalObjectMap() + : m_objectIDCounter(0) +{ +} + +NetscapePluginInstanceProxy::LocalObjectMap::~LocalObjectMap() +{ +} + +inline bool NetscapePluginInstanceProxy::LocalObjectMap::contains(uint32_t objectID) const +{ + return m_idToJSObjectMap.contains(objectID); +} + +inline JSC::JSObject* NetscapePluginInstanceProxy::LocalObjectMap::get(uint32_t objectID) const +{ + if (objectID == HashTraits<uint32_t>::emptyValue() || HashTraits<uint32_t>::isDeletedValue(objectID)) + return 0; + + return m_idToJSObjectMap.get(objectID).get(); +} + +uint32_t NetscapePluginInstanceProxy::LocalObjectMap::idForObject(JSGlobalData& globalData, JSObject* object) +{ + // This method creates objects with refcount of 1, but doesn't increase refcount when returning + // found objects. This extra count accounts for the main "reference" kept by plugin process. + + // To avoid excessive IPC, plugin process doesn't send each NPObject release/retain call to + // Safari. It only sends one when the last reference is removed, and it can destroy the proxy + // NPObject. + + // However, the browser may be sending the same object out to plug-in as a function call + // argument at the same time - neither side can know what the other one is doing. So, + // is to make PCForgetBrowserObject call return a boolean result, making it possible for + // the browser to make plugin host keep the proxy with zero refcount for a little longer. + + uint32_t objectID = 0; + + HashMap<JSC::JSObject*, pair<uint32_t, uint32_t> >::iterator iter = m_jsObjectToIDMap.find(object); + if (iter != m_jsObjectToIDMap.end()) + return iter->second.first; + + do { + objectID = ++m_objectIDCounter; + } while (!m_objectIDCounter || m_objectIDCounter == static_cast<uint32_t>(-1) || m_idToJSObjectMap.contains(objectID)); + + m_idToJSObjectMap.set(objectID, Strong<JSObject>(globalData, object)); + m_jsObjectToIDMap.set(object, make_pair<uint32_t, uint32_t>(objectID, 1)); + + return objectID; +} + +void NetscapePluginInstanceProxy::LocalObjectMap::retain(JSC::JSObject* object) +{ + HashMap<JSC::JSObject*, pair<uint32_t, uint32_t> >::iterator iter = m_jsObjectToIDMap.find(object); + ASSERT(iter != m_jsObjectToIDMap.end()); + + iter->second.second = iter->second.second + 1; +} + +void NetscapePluginInstanceProxy::LocalObjectMap::release(JSC::JSObject* object) +{ + HashMap<JSC::JSObject*, pair<uint32_t, uint32_t> >::iterator iter = m_jsObjectToIDMap.find(object); + ASSERT(iter != m_jsObjectToIDMap.end()); + + ASSERT(iter->second.second > 0); + iter->second.second = iter->second.second - 1; + if (!iter->second.second) { + m_idToJSObjectMap.remove(iter->second.first); + m_jsObjectToIDMap.remove(iter); + } +} + +void NetscapePluginInstanceProxy::LocalObjectMap::clear() +{ + m_idToJSObjectMap.clear(); + m_jsObjectToIDMap.clear(); +} + +bool NetscapePluginInstanceProxy::LocalObjectMap::forget(uint32_t objectID) +{ + if (objectID == HashTraits<uint32_t>::emptyValue() || HashTraits<uint32_t>::isDeletedValue(objectID)) { + LOG_ERROR("NetscapePluginInstanceProxy::LocalObjectMap::forget: local object id %u is not valid.", objectID); + return true; + } + + HashMap<uint32_t, JSC::Strong<JSC::JSObject> >::iterator iter = m_idToJSObjectMap.find(objectID); + if (iter == m_idToJSObjectMap.end()) { + LOG_ERROR("NetscapePluginInstanceProxy::LocalObjectMap::forget: local object %u doesn't exist.", objectID); + return true; + } + + HashMap<JSC::JSObject*, pair<uint32_t, uint32_t> >::iterator rIter = m_jsObjectToIDMap.find(iter->second.get()); + + // If the object is being sent to plug-in right now, then it's not the time to forget. + if (rIter->second.second != 1) + return false; + + m_jsObjectToIDMap.remove(rIter); + m_idToJSObjectMap.remove(iter); + return true; +} + +static uint32_t pluginIDCounter; + +bool NetscapePluginInstanceProxy::m_inDestroy; + +#ifndef NDEBUG +static WTF::RefCountedLeakCounter netscapePluginInstanceProxyCounter("NetscapePluginInstanceProxy"); +#endif + +NetscapePluginInstanceProxy::NetscapePluginInstanceProxy(NetscapePluginHostProxy* pluginHostProxy, WebHostedNetscapePluginView *pluginView, bool fullFramePlugin) + : m_pluginHostProxy(pluginHostProxy) + , m_pluginView(pluginView) + , m_requestTimer(this, &NetscapePluginInstanceProxy::requestTimerFired) + , m_currentURLRequestID(0) + , m_renderContextID(0) + , m_rendererType(UseSoftwareRenderer) + , m_waitingForReply(false) + , m_urlCheckCounter(0) + , m_pluginFunctionCallDepth(0) + , m_shouldStopSoon(false) + , m_currentRequestID(0) + , m_pluginIsWaitingForDraw(false) +{ + ASSERT(m_pluginView); + + if (fullFramePlugin) { + // For full frame plug-ins, the first requestID will always be the one for the already + // open stream. + ++m_currentURLRequestID; + } + + // Assign a plug-in ID. + do { + m_pluginID = ++pluginIDCounter; + } while (pluginHostProxy->pluginInstance(m_pluginID) || !m_pluginID); + +#ifndef NDEBUG + netscapePluginInstanceProxyCounter.increment(); +#endif +} + +PassRefPtr<NetscapePluginInstanceProxy> NetscapePluginInstanceProxy::create(NetscapePluginHostProxy* pluginHostProxy, WebHostedNetscapePluginView *pluginView, bool fullFramePlugin) +{ + RefPtr<NetscapePluginInstanceProxy> proxy = adoptRef(new NetscapePluginInstanceProxy(pluginHostProxy, pluginView, fullFramePlugin)); + pluginHostProxy->addPluginInstance(proxy.get()); + return proxy.release(); +} + +NetscapePluginInstanceProxy::~NetscapePluginInstanceProxy() +{ + ASSERT(!m_pluginHostProxy); + + m_pluginID = 0; + deleteAllValues(m_replies); + +#ifndef NDEBUG + netscapePluginInstanceProxyCounter.decrement(); +#endif +} + +void NetscapePluginInstanceProxy::resize(NSRect size, NSRect clipRect) +{ + uint32_t requestID = 0; + + requestID = nextRequestID(); + + _WKPHResizePluginInstance(m_pluginHostProxy->port(), m_pluginID, requestID, + size.origin.x, size.origin.y, size.size.width, size.size.height, + clipRect.origin.x, clipRect.origin.y, clipRect.size.width, clipRect.size.height); + + waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); +} + +void NetscapePluginInstanceProxy::stopAllStreams() +{ + Vector<RefPtr<HostedNetscapePluginStream> > streamsCopy; + copyValuesToVector(m_streams, streamsCopy); + for (size_t i = 0; i < streamsCopy.size(); i++) + streamsCopy[i]->stop(); +} + +void NetscapePluginInstanceProxy::cleanup() +{ + stopAllStreams(); + + m_requestTimer.stop(); + + // Clear the object map, this will cause any outstanding JS objects that the plug-in had a reference to + // to go away when the next garbage collection takes place. + m_localObjects.clear(); + + if (Frame* frame = core([m_pluginView webFrame])) + frame->script()->cleanupScriptObjectsForPlugin(m_pluginView); + + ProxyInstanceSet instances; + instances.swap(m_instances); + + // Invalidate all proxy instances. + ProxyInstanceSet::const_iterator end = instances.end(); + for (ProxyInstanceSet::const_iterator it = instances.begin(); it != end; ++it) + (*it)->invalidate(); + + m_pluginView = nil; + m_manualStream = 0; +} + +void NetscapePluginInstanceProxy::invalidate() +{ + // If the plug-in host has died, the proxy will be null. + if (!m_pluginHostProxy) + return; + + m_pluginHostProxy->removePluginInstance(this); + m_pluginHostProxy = 0; +} + +void NetscapePluginInstanceProxy::destroy() +{ + uint32_t requestID = nextRequestID(); + + ASSERT(!m_inDestroy); + m_inDestroy = true; + + FrameLoadMap::iterator end = m_pendingFrameLoads.end(); + for (FrameLoadMap::iterator it = m_pendingFrameLoads.begin(); it != end; ++it) + [(it->first) _setInternalLoadDelegate:nil]; + + _WKPHDestroyPluginInstance(m_pluginHostProxy->port(), m_pluginID, requestID); + + // If the plug-in host crashes while we're waiting for a reply, the last reference to the instance proxy + // will go away. Prevent this by protecting it here. + RefPtr<NetscapePluginInstanceProxy> protect(this); + + // We don't care about the reply here - we just want to block until the plug-in instance has been torn down. + waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); + + m_inDestroy = false; + + cleanup(); + invalidate(); +} + +void NetscapePluginInstanceProxy::setManualStream(PassRefPtr<HostedNetscapePluginStream> manualStream) +{ + ASSERT(!m_manualStream); + + m_manualStream = manualStream; +} + +bool NetscapePluginInstanceProxy::cancelStreamLoad(uint32_t streamID, NPReason reason) +{ + HostedNetscapePluginStream* stream = 0; + + if (m_manualStream && streamID == 1) + stream = m_manualStream.get(); + else + stream = m_streams.get(streamID).get(); + + if (!stream) + return false; + + stream->cancelLoad(reason); + return true; +} + +void NetscapePluginInstanceProxy::disconnectStream(HostedNetscapePluginStream* stream) +{ + if (stream == m_manualStream) { + m_manualStream = 0; + return; + } + + ASSERT(m_streams.get(stream->streamID()) == stream); + m_streams.remove(stream->streamID()); +} + +void NetscapePluginInstanceProxy::pluginHostDied() +{ + m_pluginHostProxy = 0; + + [m_pluginView pluginHostDied]; + + cleanup(); +} + +void NetscapePluginInstanceProxy::focusChanged(bool hasFocus) +{ + _WKPHPluginInstanceFocusChanged(m_pluginHostProxy->port(), m_pluginID, hasFocus); +} + +void NetscapePluginInstanceProxy::windowFocusChanged(bool hasFocus) +{ + _WKPHPluginInstanceWindowFocusChanged(m_pluginHostProxy->port(), m_pluginID, hasFocus); +} + +void NetscapePluginInstanceProxy::windowFrameChanged(NSRect frame) +{ + _WKPHPluginInstanceWindowFrameChanged(m_pluginHostProxy->port(), m_pluginID, frame.origin.x, frame.origin.y, frame.size.width, frame.size.height, + NSMaxY([[[NSScreen screens] objectAtIndex:0] frame])); +} + +void NetscapePluginInstanceProxy::startTimers(bool throttleTimers) +{ + _WKPHPluginInstanceStartTimers(m_pluginHostProxy->port(), m_pluginID, throttleTimers); +} + +void NetscapePluginInstanceProxy::mouseEvent(NSView *pluginView, NSEvent *event, NPCocoaEventType type) +{ + NSPoint screenPoint = [[event window] convertBaseToScreen:[event locationInWindow]]; + NSPoint pluginPoint = [pluginView convertPoint:[event locationInWindow] fromView:nil]; + + int clickCount; + if (type == NPCocoaEventMouseEntered || type == NPCocoaEventMouseExited) + clickCount = 0; + else + clickCount = [event clickCount]; + + + _WKPHPluginInstanceMouseEvent(m_pluginHostProxy->port(), m_pluginID, + [event timestamp], + type, [event modifierFlags], + pluginPoint.x, pluginPoint.y, + screenPoint.x, screenPoint.y, + NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]), + [event buttonNumber], clickCount, + [event deltaX], [event deltaY], [event deltaZ]); +} + +void NetscapePluginInstanceProxy::keyEvent(NSView *pluginView, NSEvent *event, NPCocoaEventType type) +{ + NSData *charactersData = [[event characters] dataUsingEncoding:NSUTF8StringEncoding]; + NSData *charactersIgnoringModifiersData = [[event charactersIgnoringModifiers] dataUsingEncoding:NSUTF8StringEncoding]; + + _WKPHPluginInstanceKeyboardEvent(m_pluginHostProxy->port(), m_pluginID, + [event timestamp], + type, [event modifierFlags], + const_cast<char*>(reinterpret_cast<const char*>([charactersData bytes])), [charactersData length], + const_cast<char*>(reinterpret_cast<const char*>([charactersIgnoringModifiersData bytes])), [charactersIgnoringModifiersData length], + [event isARepeat], [event keyCode], WKGetNSEventKeyChar(event)); +} + +void NetscapePluginInstanceProxy::syntheticKeyDownWithCommandModifier(int keyCode, char character) +{ + NSData *charactersData = [NSData dataWithBytes:&character length:1]; + + _WKPHPluginInstanceKeyboardEvent(m_pluginHostProxy->port(), m_pluginID, + [NSDate timeIntervalSinceReferenceDate], + NPCocoaEventKeyDown, NSCommandKeyMask, + const_cast<char*>(reinterpret_cast<const char*>([charactersData bytes])), [charactersData length], + const_cast<char*>(reinterpret_cast<const char*>([charactersData bytes])), [charactersData length], + false, keyCode, character); +} + +void NetscapePluginInstanceProxy::flagsChanged(NSEvent *event) +{ + _WKPHPluginInstanceKeyboardEvent(m_pluginHostProxy->port(), m_pluginID, + [event timestamp], NPCocoaEventFlagsChanged, + [event modifierFlags], 0, 0, 0, 0, false, [event keyCode], 0); +} + +void NetscapePluginInstanceProxy::insertText(NSString *text) +{ + NSData *textData = [text dataUsingEncoding:NSUTF8StringEncoding]; + + _WKPHPluginInstanceInsertText(m_pluginHostProxy->port(), m_pluginID, + const_cast<char*>(reinterpret_cast<const char*>([textData bytes])), [textData length]); +} + +bool NetscapePluginInstanceProxy::wheelEvent(NSView *pluginView, NSEvent *event) +{ + NSPoint pluginPoint = [pluginView convertPoint:[event locationInWindow] fromView:nil]; + + uint32_t requestID = nextRequestID(); + _WKPHPluginInstanceWheelEvent(m_pluginHostProxy->port(), m_pluginID, requestID, + [event timestamp], [event modifierFlags], + pluginPoint.x, pluginPoint.y, [event buttonNumber], + [event deltaX], [event deltaY], [event deltaZ]); + + auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID); + if (!reply.get() || !reply->m_result) + return false; + + return true; +} + +void NetscapePluginInstanceProxy::print(CGContextRef context, unsigned width, unsigned height) +{ + uint32_t requestID = nextRequestID(); + _WKPHPluginInstancePrint(m_pluginHostProxy->port(), m_pluginID, requestID, width, height); + + auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID); + if (!reply.get() || !reply->m_returnValue) + return; + + RetainPtr<CGDataProvider> dataProvider(AdoptCF, CGDataProviderCreateWithCFData(reply->m_result.get())); + RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB()); + RetainPtr<CGImageRef> image(AdoptCF, CGImageCreate(width, height, 8, 32, width * 4, colorSpace.get(), kCGImageAlphaFirst, dataProvider.get(), 0, false, kCGRenderingIntentDefault)); + + // Flip the context and draw the image. + CGContextSaveGState(context); + CGContextTranslateCTM(context, 0.0, height); + CGContextScaleCTM(context, 1.0, -1.0); + + CGContextDrawImage(context, CGRectMake(0, 0, width, height), image.get()); + + CGContextRestoreGState(context); +} + +void NetscapePluginInstanceProxy::snapshot(CGContextRef context, unsigned width, unsigned height) +{ + uint32_t requestID = nextRequestID(); + _WKPHPluginInstanceSnapshot(m_pluginHostProxy->port(), m_pluginID, requestID, width, height); + + auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID); + if (!reply.get() || !reply->m_returnValue) + return; + + RetainPtr<CGDataProvider> dataProvider(AdoptCF, CGDataProviderCreateWithCFData(reply->m_result.get())); + RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB()); + RetainPtr<CGImageRef> image(AdoptCF, CGImageCreate(width, height, 8, 32, width * 4, colorSpace.get(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, dataProvider.get(), 0, false, kCGRenderingIntentDefault)); + + CGContextDrawImage(context, CGRectMake(0, 0, width, height), image.get()); +} + +void NetscapePluginInstanceProxy::stopTimers() +{ + _WKPHPluginInstanceStopTimers(m_pluginHostProxy->port(), m_pluginID); +} + +void NetscapePluginInstanceProxy::status(const char* message) +{ + RetainPtr<CFStringRef> status(AdoptCF, CFStringCreateWithCString(0, message ? message : "", kCFStringEncodingUTF8)); + if (!status) + return; + + WebView *wv = [m_pluginView webView]; + [[wv _UIDelegateForwarder] webView:wv setStatusText:(NSString *)status.get()]; +} + +NPError NetscapePluginInstanceProxy::loadURL(const char* url, const char* target, const char* postData, uint32_t postLen, LoadURLFlags flags, uint32_t& streamID) +{ + if (!url) + return NPERR_INVALID_PARAM; + + NSMutableURLRequest *request = [m_pluginView requestWithURLCString:url]; + + if (flags & IsPost) { + NSData *httpBody = nil; + + if (flags & PostDataIsFile) { + // If we're posting a file, buf is either a file URL or a path to the file. + if (!postData) + return NPERR_INVALID_PARAM; + RetainPtr<CFStringRef> bufString(AdoptCF, CFStringCreateWithCString(kCFAllocatorDefault, postData, kCFStringEncodingWindowsLatin1)); + if (!bufString) + return NPERR_INVALID_PARAM; + + NSURL *fileURL = [NSURL _web_URLWithDataAsString:(NSString *)bufString.get()]; + NSString *path; + if ([fileURL isFileURL]) + path = [fileURL path]; + else + path = (NSString *)bufString.get(); + httpBody = [NSData dataWithContentsOfFile:[path _webkit_fixedCarbonPOSIXPath]]; + if (!httpBody) + return NPERR_FILE_NOT_FOUND; + } else + httpBody = [NSData dataWithBytes:postData length:postLen]; + + if (![httpBody length]) + return NPERR_INVALID_PARAM; + + [request setHTTPMethod:@"POST"]; + + if (flags & AllowHeadersInPostData) { + if ([httpBody _web_startsWithBlankLine]) + httpBody = [httpBody subdataWithRange:NSMakeRange(1, [httpBody length] - 1)]; + else { + NSInteger location = [httpBody _web_locationAfterFirstBlankLine]; + if (location != NSNotFound) { + // If the blank line is somewhere in the middle of postData, everything before is the header. + NSData *headerData = [httpBody subdataWithRange:NSMakeRange(0, location)]; + NSMutableDictionary *header = [headerData _webkit_parseRFC822HeaderFields]; + unsigned dataLength = [httpBody length] - location; + + // Sometimes plugins like to set Content-Length themselves when they post, + // but CFNetwork does not like that. So we will remove the header + // and instead truncate the data to the requested length. + NSString *contentLength = [header objectForKey:@"Content-Length"]; + + if (contentLength) + dataLength = min(static_cast<unsigned>([contentLength intValue]), dataLength); + [header removeObjectForKey:@"Content-Length"]; + + if ([header count] > 0) + [request setAllHTTPHeaderFields:header]; + + // Everything after the blank line is the actual content of the POST. + httpBody = [httpBody subdataWithRange:NSMakeRange(location, dataLength)]; + } + } + } + + if (![httpBody length]) + return NPERR_INVALID_PARAM; + + // Plug-ins expect to receive uncached data when doing a POST (3347134). + [request setCachePolicy:NSURLRequestReloadIgnoringCacheData]; + [request setHTTPBody:httpBody]; + } + + return loadRequest(request, target, flags & AllowPopups, streamID); +} + +void NetscapePluginInstanceProxy::performRequest(PluginRequest* pluginRequest) +{ + ASSERT(m_pluginView); + + NSURLRequest *request = pluginRequest->request(); + NSString *frameName = pluginRequest->frameName(); + WebFrame *frame = nil; + + NSURL *URL = [request URL]; + NSString *JSString = [URL _webkit_scriptIfJavaScriptURL]; + + ASSERT(frameName || JSString); + if (frameName) { + // FIXME - need to get rid of this window creation which + // bypasses normal targeted link handling + frame = kit(core([m_pluginView webFrame])->loader()->findFrameForNavigation(frameName)); + if (!frame) { + WebView *currentWebView = [m_pluginView webView]; + NSDictionary *features = [[NSDictionary alloc] init]; + WebView *newWebView = [[currentWebView _UIDelegateForwarder] webView:currentWebView + createWebViewWithRequest:nil + windowFeatures:features]; + [features release]; + + if (!newWebView) { + _WKPHLoadURLNotify(m_pluginHostProxy->port(), m_pluginID, pluginRequest->requestID(), NPERR_GENERIC_ERROR); + return; + } + + frame = [newWebView mainFrame]; + core(frame)->tree()->setName(frameName); + [[newWebView _UIDelegateForwarder] webViewShow:newWebView]; + } + } + + if (JSString) { + ASSERT(!frame || [m_pluginView webFrame] == frame); + evaluateJavaScript(pluginRequest); + } else { + [frame loadRequest:request]; + + // Check if another plug-in view or even this view is waiting for the frame to load. + // If it is, tell it that the load was cancelled because it will be anyway. + WebHostedNetscapePluginView *view = [frame _internalLoadDelegate]; + if (view != nil) { + ASSERT([view isKindOfClass:[WebHostedNetscapePluginView class]]); + [view webFrame:frame didFinishLoadWithReason:NPRES_USER_BREAK]; + } + m_pendingFrameLoads.set(frame, pluginRequest); + [frame _setInternalLoadDelegate:m_pluginView]; + } + +} + +void NetscapePluginInstanceProxy::webFrameDidFinishLoadWithReason(WebFrame* webFrame, NPReason reason) +{ + FrameLoadMap::iterator it = m_pendingFrameLoads.find(webFrame); + ASSERT(it != m_pendingFrameLoads.end()); + + PluginRequest* pluginRequest = it->second.get(); + _WKPHLoadURLNotify(m_pluginHostProxy->port(), m_pluginID, pluginRequest->requestID(), reason); + + m_pendingFrameLoads.remove(it); + + [webFrame _setInternalLoadDelegate:nil]; +} + +void NetscapePluginInstanceProxy::evaluateJavaScript(PluginRequest* pluginRequest) +{ + NSURL *URL = [pluginRequest->request() URL]; + NSString *JSString = [URL _webkit_scriptIfJavaScriptURL]; + ASSERT(JSString); + + NSString *result = [[m_pluginView webFrame] _stringByEvaluatingJavaScriptFromString:JSString forceUserGesture:pluginRequest->allowPopups()]; + + // Don't continue if stringByEvaluatingJavaScriptFromString caused the plug-in to stop. + if (!m_pluginHostProxy) + return; + + if (pluginRequest->frameName() != nil) + return; + + if ([result length] > 0) { + // Don't call NPP_NewStream and other stream methods if there is no JS result to deliver. This is what Mozilla does. + NSData *JSData = [result dataUsingEncoding:NSUTF8StringEncoding]; + + RefPtr<HostedNetscapePluginStream> stream = HostedNetscapePluginStream::create(this, pluginRequest->requestID(), pluginRequest->request()); + m_streams.add(stream->streamID(), stream); + + RetainPtr<NSURLResponse> response(AdoptNS, [[NSURLResponse alloc] initWithURL:URL + MIMEType:@"text/plain" + expectedContentLength:[JSData length] + textEncodingName:nil]); + stream->startStreamWithResponse(response.get()); + stream->didReceiveData(0, static_cast<const char*>([JSData bytes]), [JSData length]); + stream->didFinishLoading(0); + } +} + +void NetscapePluginInstanceProxy::requestTimerFired(Timer<NetscapePluginInstanceProxy>*) +{ + ASSERT(!m_pluginRequests.isEmpty()); + ASSERT(m_pluginView); + + RefPtr<PluginRequest> request = m_pluginRequests.first(); + m_pluginRequests.removeFirst(); + + if (!m_pluginRequests.isEmpty()) + m_requestTimer.startOneShot(0); + + performRequest(request.get()); +} + +NPError NetscapePluginInstanceProxy::loadRequest(NSURLRequest *request, const char* cTarget, bool allowPopups, uint32_t& requestID) +{ + NSURL *URL = [request URL]; + + if (!URL) + return NPERR_INVALID_URL; + + // Don't allow requests to be loaded when the document loader is stopping all loaders. + DocumentLoader* documentLoader = [[m_pluginView dataSource] _documentLoader]; + if (!documentLoader || documentLoader->isStopping()) + return NPERR_GENERIC_ERROR; + + NSString *target = nil; + if (cTarget) { + // Find the frame given the target string. + target = [NSString stringWithCString:cTarget encoding:NSISOLatin1StringEncoding]; + } + WebFrame *frame = [m_pluginView webFrame]; + + // don't let a plugin start any loads if it is no longer part of a document that is being + // displayed unless the loads are in the same frame as the plugin. + if (documentLoader != core([m_pluginView webFrame])->loader()->activeDocumentLoader() && + (!cTarget || [frame findFrameNamed:target] != frame)) { + return NPERR_GENERIC_ERROR; + } + + NSString *JSString = [URL _webkit_scriptIfJavaScriptURL]; + if (JSString != nil) { + if (![[[m_pluginView webView] preferences] isJavaScriptEnabled]) { + // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does. + return NPERR_GENERIC_ERROR; + } + } else { + if (!core([m_pluginView webFrame])->document()->securityOrigin()->canDisplay(URL)) + return NPERR_GENERIC_ERROR; + } + + // FIXME: Handle wraparound + requestID = ++m_currentURLRequestID; + + if (cTarget || JSString) { + // Make when targetting a frame or evaluating a JS string, perform the request after a delay because we don't + // want to potentially kill the plug-in inside of its URL request. + + if (JSString && target && [frame findFrameNamed:target] != frame) { + // For security reasons, only allow JS requests to be made on the frame that contains the plug-in. + return NPERR_INVALID_PARAM; + } + + RefPtr<PluginRequest> pluginRequest = PluginRequest::create(requestID, request, target, allowPopups); + m_pluginRequests.append(pluginRequest.release()); + m_requestTimer.startOneShot(0); + } else { + RefPtr<HostedNetscapePluginStream> stream = HostedNetscapePluginStream::create(this, requestID, request); + + ASSERT(!m_streams.contains(requestID)); + m_streams.add(requestID, stream); + stream->start(); + } + + return NPERR_NO_ERROR; +} + +NetscapePluginInstanceProxy::Reply* NetscapePluginInstanceProxy::processRequestsAndWaitForReply(uint32_t requestID) +{ + Reply* reply = 0; + + ASSERT(m_pluginHostProxy); + while (!(reply = m_replies.take(requestID))) { + if (!m_pluginHostProxy->processRequests()) + return 0; + + // The host proxy can be destroyed while executing a nested processRequests() call, in which case it's normal + // to get a success result, but be unable to keep looping. + if (!m_pluginHostProxy) + return 0; + } + + ASSERT(reply); + return reply; +} + +// NPRuntime support +bool NetscapePluginInstanceProxy::getWindowNPObject(uint32_t& objectID) +{ + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + if (!frame->script()->canExecuteScripts(NotAboutToExecuteScript)) + objectID = 0; + else + objectID = m_localObjects.idForObject(*pluginWorld()->globalData(), frame->script()->windowShell(pluginWorld())->window()); + + return true; +} + +bool NetscapePluginInstanceProxy::getPluginElementNPObject(uint32_t& objectID) +{ + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + if (JSObject* object = frame->script()->jsObjectForPluginElement([m_pluginView element])) + objectID = m_localObjects.idForObject(*pluginWorld()->globalData(), object); + else + objectID = 0; + + return true; +} + +bool NetscapePluginInstanceProxy::forgetBrowserObjectID(uint32_t objectID) +{ + return m_localObjects.forget(objectID); +} + +bool NetscapePluginInstanceProxy::evaluate(uint32_t objectID, const String& script, data_t& resultData, mach_msg_type_number_t& resultLength, bool allowPopups) +{ + resultData = 0; + resultLength = 0; + + if (m_inDestroy) + return false; + + if (!m_localObjects.contains(objectID)) { + LOG_ERROR("NetscapePluginInstanceProxy::evaluate: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + JSLock lock(SilenceAssertionsOnly); + + Strong<JSGlobalObject> globalObject(*pluginWorld()->globalData(), frame->script()->globalObject(pluginWorld())); + ExecState* exec = globalObject->globalExec(); + + bool oldAllowPopups = frame->script()->allowPopupsFromPlugin(); + frame->script()->setAllowPopupsFromPlugin(allowPopups); + + globalObject->globalData().timeoutChecker.start(); + Completion completion = JSC::evaluate(exec, globalObject->globalScopeChain(), makeSource(script)); + globalObject->globalData().timeoutChecker.stop(); + ComplType type = completion.complType(); + + frame->script()->setAllowPopupsFromPlugin(oldAllowPopups); + + JSValue result; + if (type == Normal) + result = completion.value(); + + if (!result) + result = jsUndefined(); + + marshalValue(exec, result, resultData, resultLength); + exec->clearException(); + return true; +} + +bool NetscapePluginInstanceProxy::invoke(uint32_t objectID, const Identifier& methodName, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength) +{ + resultData = 0; + resultLength = 0; + + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::invoke: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + JSLock lock(SilenceAssertionsOnly); + JSValue function = object->get(exec, methodName); + CallData callData; + CallType callType = getCallData(function, callData); + if (callType == CallTypeNone) + return false; + + MarkedArgumentBuffer argList; + demarshalValues(exec, argumentsData, argumentsLength, argList); + + RefPtr<JSGlobalData> globalData = pluginWorld()->globalData(); + globalData->timeoutChecker.start(); + JSValue value = call(exec, function, callType, callData, object, argList); + globalData->timeoutChecker.stop(); + + marshalValue(exec, value, resultData, resultLength); + exec->clearException(); + return true; +} + +bool NetscapePluginInstanceProxy::invokeDefault(uint32_t objectID, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::invokeDefault: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + JSLock lock(SilenceAssertionsOnly); + CallData callData; + CallType callType = object->getCallData(callData); + if (callType == CallTypeNone) + return false; + + MarkedArgumentBuffer argList; + demarshalValues(exec, argumentsData, argumentsLength, argList); + + RefPtr<JSGlobalData> globalData = pluginWorld()->globalData(); + globalData->timeoutChecker.start(); + JSValue value = call(exec, object, callType, callData, object, argList); + globalData->timeoutChecker.stop(); + + marshalValue(exec, value, resultData, resultLength); + exec->clearException(); + return true; +} + +bool NetscapePluginInstanceProxy::construct(uint32_t objectID, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::construct: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + JSLock lock(SilenceAssertionsOnly); + + ConstructData constructData; + ConstructType constructType = object->getConstructData(constructData); + if (constructType == ConstructTypeNone) + return false; + + MarkedArgumentBuffer argList; + demarshalValues(exec, argumentsData, argumentsLength, argList); + + RefPtr<JSGlobalData> globalData = pluginWorld()->globalData(); + globalData->timeoutChecker.start(); + JSValue value = JSC::construct(exec, object, constructType, constructData, argList); + globalData->timeoutChecker.stop(); + + marshalValue(exec, value, resultData, resultLength); + exec->clearException(); + return true; +} + +bool NetscapePluginInstanceProxy::getProperty(uint32_t objectID, const Identifier& propertyName, data_t& resultData, mach_msg_type_number_t& resultLength) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::getProperty: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + JSLock lock(SilenceAssertionsOnly); + JSValue value = object->get(exec, propertyName); + + marshalValue(exec, value, resultData, resultLength); + exec->clearException(); + return true; +} + +bool NetscapePluginInstanceProxy::getProperty(uint32_t objectID, unsigned propertyName, data_t& resultData, mach_msg_type_number_t& resultLength) +{ + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::getProperty: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + JSLock lock(SilenceAssertionsOnly); + JSValue value = object->get(exec, propertyName); + + marshalValue(exec, value, resultData, resultLength); + exec->clearException(); + return true; +} + +bool NetscapePluginInstanceProxy::setProperty(uint32_t objectID, const Identifier& propertyName, data_t valueData, mach_msg_type_number_t valueLength) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::setProperty: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + JSLock lock(SilenceAssertionsOnly); + + JSValue value = demarshalValue(exec, valueData, valueLength); + PutPropertySlot slot; + object->put(exec, propertyName, value, slot); + + exec->clearException(); + return true; +} + +bool NetscapePluginInstanceProxy::setProperty(uint32_t objectID, unsigned propertyName, data_t valueData, mach_msg_type_number_t valueLength) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::setProperty: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + JSLock lock(SilenceAssertionsOnly); + + JSValue value = demarshalValue(exec, valueData, valueLength); + object->put(exec, propertyName, value); + + exec->clearException(); + return true; +} + +bool NetscapePluginInstanceProxy::removeProperty(uint32_t objectID, const Identifier& propertyName) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::removeProperty: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + if (!object->hasProperty(exec, propertyName)) { + exec->clearException(); + return false; + } + + JSLock lock(SilenceAssertionsOnly); + object->deleteProperty(exec, propertyName); + exec->clearException(); + return true; +} + +bool NetscapePluginInstanceProxy::removeProperty(uint32_t objectID, unsigned propertyName) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::removeProperty: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + if (!object->hasProperty(exec, propertyName)) { + exec->clearException(); + return false; + } + + JSLock lock(SilenceAssertionsOnly); + object->deleteProperty(exec, propertyName); + exec->clearException(); + return true; +} + +bool NetscapePluginInstanceProxy::hasProperty(uint32_t objectID, const Identifier& propertyName) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::hasProperty: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + bool result = object->hasProperty(exec, propertyName); + exec->clearException(); + + return result; +} + +bool NetscapePluginInstanceProxy::hasProperty(uint32_t objectID, unsigned propertyName) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::hasProperty: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + bool result = object->hasProperty(exec, propertyName); + exec->clearException(); + + return result; +} + +bool NetscapePluginInstanceProxy::hasMethod(uint32_t objectID, const Identifier& methodName) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::hasMethod: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + JSLock lock(SilenceAssertionsOnly); + JSValue func = object->get(exec, methodName); + exec->clearException(); + return !func.isUndefined(); +} + +bool NetscapePluginInstanceProxy::enumerate(uint32_t objectID, data_t& resultData, mach_msg_type_number_t& resultLength) +{ + if (m_inDestroy) + return false; + + JSObject* object = m_localObjects.get(objectID); + if (!object) { + LOG_ERROR("NetscapePluginInstanceProxy::enumerate: local object %u doesn't exist.", objectID); + return false; + } + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec(); + JSLock lock(SilenceAssertionsOnly); + + PropertyNameArray propertyNames(exec); + object->getPropertyNames(exec, propertyNames); + + RetainPtr<NSMutableArray*> array(AdoptNS, [[NSMutableArray alloc] init]); + for (unsigned i = 0; i < propertyNames.size(); i++) { + uint64_t methodName = reinterpret_cast<uint64_t>(_NPN_GetStringIdentifier(propertyNames[i].ustring().utf8().data())); + + [array.get() addObject:[NSNumber numberWithLongLong:methodName]]; + } + + NSData *data = [NSPropertyListSerialization dataFromPropertyList:array.get() format:NSPropertyListBinaryFormat_v1_0 errorDescription:0]; + ASSERT(data); + + resultLength = [data length]; + mig_allocate(reinterpret_cast<vm_address_t*>(&resultData), resultLength); + + memcpy(resultData, [data bytes], resultLength); + + exec->clearException(); + + return true; +} + +void NetscapePluginInstanceProxy::addValueToArray(NSMutableArray *array, ExecState* exec, JSValue value) +{ + JSLock lock(SilenceAssertionsOnly); + + if (value.isString()) { + [array addObject:[NSNumber numberWithInt:StringValueType]]; + [array addObject:ustringToString(value.toString(exec))]; + } else if (value.isNumber()) { + [array addObject:[NSNumber numberWithInt:DoubleValueType]]; + [array addObject:[NSNumber numberWithDouble:value.toNumber(exec)]]; + } else if (value.isBoolean()) { + [array addObject:[NSNumber numberWithInt:BoolValueType]]; + [array addObject:[NSNumber numberWithBool:value.toBoolean(exec)]]; + } else if (value.isNull()) + [array addObject:[NSNumber numberWithInt:NullValueType]]; + else if (value.isObject()) { + JSObject* object = asObject(value); + if (object->classInfo() == &ProxyRuntimeObject::s_info) { + ProxyRuntimeObject* runtimeObject = static_cast<ProxyRuntimeObject*>(object); + if (ProxyInstance* instance = runtimeObject->getInternalProxyInstance()) { + [array addObject:[NSNumber numberWithInt:NPObjectValueType]]; + [array addObject:[NSNumber numberWithInt:instance->objectID()]]; + } + } else { + [array addObject:[NSNumber numberWithInt:JSObjectValueType]]; + [array addObject:[NSNumber numberWithInt:m_localObjects.idForObject(exec->globalData(), object)]]; + } + } else + [array addObject:[NSNumber numberWithInt:VoidValueType]]; +} + +void NetscapePluginInstanceProxy::marshalValue(ExecState* exec, JSValue value, data_t& resultData, mach_msg_type_number_t& resultLength) +{ + RetainPtr<NSMutableArray*> array(AdoptNS, [[NSMutableArray alloc] init]); + + addValueToArray(array.get(), exec, value); + + RetainPtr<NSData *> data = [NSPropertyListSerialization dataFromPropertyList:array.get() format:NSPropertyListBinaryFormat_v1_0 errorDescription:0]; + ASSERT(data); + + resultLength = [data.get() length]; + mig_allocate(reinterpret_cast<vm_address_t*>(&resultData), resultLength); + + memcpy(resultData, [data.get() bytes], resultLength); +} + +RetainPtr<NSData *> NetscapePluginInstanceProxy::marshalValues(ExecState* exec, const ArgList& args) +{ + RetainPtr<NSMutableArray*> array(AdoptNS, [[NSMutableArray alloc] init]); + + for (unsigned i = 0; i < args.size(); i++) + addValueToArray(array.get(), exec, args.at(i)); + + RetainPtr<NSData *> data = [NSPropertyListSerialization dataFromPropertyList:array.get() format:NSPropertyListBinaryFormat_v1_0 errorDescription:0]; + ASSERT(data); + + return data; +} + +bool NetscapePluginInstanceProxy::demarshalValueFromArray(ExecState* exec, NSArray *array, NSUInteger& index, JSValue& result) +{ + if (index == [array count]) + return false; + + int type = [[array objectAtIndex:index++] intValue]; + switch (type) { + case VoidValueType: + result = jsUndefined(); + return true; + case NullValueType: + result = jsNull(); + return true; + case BoolValueType: + result = jsBoolean([[array objectAtIndex:index++] boolValue]); + return true; + case DoubleValueType: + result = jsNumber([[array objectAtIndex:index++] doubleValue]); + return true; + case StringValueType: { + NSString *string = [array objectAtIndex:index++]; + + result = jsString(exec, String(string)); + return true; + } + case JSObjectValueType: { + uint32_t objectID = [[array objectAtIndex:index++] intValue]; + + result = m_localObjects.get(objectID); + ASSERT(result); + return true; + } + case NPObjectValueType: { + uint32_t objectID = [[array objectAtIndex:index++] intValue]; + + Frame* frame = core([m_pluginView webFrame]); + if (!frame) + return false; + + if (!frame->script()->canExecuteScripts(NotAboutToExecuteScript)) + return false; + + RefPtr<RootObject> rootObject = frame->script()->createRootObject(m_pluginView); + if (!rootObject) + return false; + + result = ProxyInstance::create(rootObject.release(), this, objectID)->createRuntimeObject(exec); + return true; + } + default: + ASSERT_NOT_REACHED(); + return false; + } +} + +JSValue NetscapePluginInstanceProxy::demarshalValue(ExecState* exec, const char* valueData, mach_msg_type_number_t valueLength) +{ + RetainPtr<NSData*> data(AdoptNS, [[NSData alloc] initWithBytesNoCopy:(void*)valueData length:valueLength freeWhenDone:NO]); + + RetainPtr<NSArray*> array = [NSPropertyListSerialization propertyListFromData:data.get() + mutabilityOption:NSPropertyListImmutable + format:0 + errorDescription:0]; + NSUInteger position = 0; + JSValue value; + bool result = demarshalValueFromArray(exec, array.get(), position, value); + ASSERT_UNUSED(result, result); + + return value; +} + +void NetscapePluginInstanceProxy::demarshalValues(ExecState* exec, data_t valuesData, mach_msg_type_number_t valuesLength, MarkedArgumentBuffer& result) +{ + RetainPtr<NSData*> data(AdoptNS, [[NSData alloc] initWithBytesNoCopy:valuesData length:valuesLength freeWhenDone:NO]); + + RetainPtr<NSArray*> array = [NSPropertyListSerialization propertyListFromData:data.get() + mutabilityOption:NSPropertyListImmutable + format:0 + errorDescription:0]; + NSUInteger position = 0; + JSValue value; + while (demarshalValueFromArray(exec, array.get(), position, value)) + result.append(value); +} + +void NetscapePluginInstanceProxy::retainLocalObject(JSC::JSValue value) +{ + if (!value.isObject() || value.inherits(&ProxyRuntimeObject::s_info)) + return; + + m_localObjects.retain(asObject(value)); +} + +void NetscapePluginInstanceProxy::releaseLocalObject(JSC::JSValue value) +{ + if (!value.isObject() || value.inherits(&ProxyRuntimeObject::s_info)) + return; + + m_localObjects.release(asObject(value)); +} + +PassRefPtr<Instance> NetscapePluginInstanceProxy::createBindingsInstance(PassRefPtr<RootObject> rootObject) +{ + uint32_t requestID = nextRequestID(); + + if (_WKPHGetScriptableNPObject(m_pluginHostProxy->port(), m_pluginID, requestID) != KERN_SUCCESS) + return 0; + + auto_ptr<GetScriptableNPObjectReply> reply = waitForReply<GetScriptableNPObjectReply>(requestID); + if (!reply.get()) + return 0; + + if (!reply->m_objectID) + return 0; + + // Since the reply was non-null, "this" is still a valid pointer. + return ProxyInstance::create(rootObject, this, reply->m_objectID); +} + +void NetscapePluginInstanceProxy::addInstance(ProxyInstance* instance) +{ + ASSERT(!m_instances.contains(instance)); + + m_instances.add(instance); +} + +void NetscapePluginInstanceProxy::removeInstance(ProxyInstance* instance) +{ + ASSERT(m_instances.contains(instance)); + + m_instances.remove(instance); +} + +void NetscapePluginInstanceProxy::willCallPluginFunction() +{ + m_pluginFunctionCallDepth++; +} + +void NetscapePluginInstanceProxy::didCallPluginFunction(bool& stopped) +{ + ASSERT(m_pluginFunctionCallDepth > 0); + m_pluginFunctionCallDepth--; + + // If -stop was called while we were calling into a plug-in function, and we're no longer + // inside a plug-in function, stop now. + if (!m_pluginFunctionCallDepth && m_shouldStopSoon) { + m_shouldStopSoon = false; + [m_pluginView stop]; + stopped = true; + } +} + +bool NetscapePluginInstanceProxy::shouldStop() +{ + if (m_pluginFunctionCallDepth) { + m_shouldStopSoon = true; + return false; + } + + return true; +} + +uint32_t NetscapePluginInstanceProxy::nextRequestID() +{ + uint32_t requestID = ++m_currentRequestID; + + // We don't want to return the HashMap empty/deleted "special keys" + if (requestID == 0 || requestID == static_cast<uint32_t>(-1)) + return nextRequestID(); + + return requestID; +} + +void NetscapePluginInstanceProxy::invalidateRect(double x, double y, double width, double height) +{ + ASSERT(m_pluginView); + + m_pluginIsWaitingForDraw = true; + [m_pluginView invalidatePluginContentRect:NSMakeRect(x, y, width, height)]; +} + +void NetscapePluginInstanceProxy::didDraw() +{ + if (!m_pluginIsWaitingForDraw) + return; + + m_pluginIsWaitingForDraw = false; + _WKPHPluginInstanceDidDraw(m_pluginHostProxy->port(), m_pluginID); +} + +bool NetscapePluginInstanceProxy::getCookies(data_t urlData, mach_msg_type_number_t urlLength, data_t& cookiesData, mach_msg_type_number_t& cookiesLength) +{ + ASSERT(m_pluginView); + + NSURL *url = [m_pluginView URLWithCString:urlData]; + if (!url) + return false; + + if (Frame* frame = core([m_pluginView webFrame])) { + String cookieString = cookies(frame->document(), url); + WTF::CString cookieStringUTF8 = cookieString.utf8(); + if (cookieStringUTF8.isNull()) + return false; + + cookiesLength = cookieStringUTF8.length(); + mig_allocate(reinterpret_cast<vm_address_t*>(&cookiesData), cookiesLength); + memcpy(cookiesData, cookieStringUTF8.data(), cookiesLength); + + return true; + } + + return false; +} + +bool NetscapePluginInstanceProxy::setCookies(data_t urlData, mach_msg_type_number_t urlLength, data_t cookiesData, mach_msg_type_number_t cookiesLength) +{ + ASSERT(m_pluginView); + + NSURL *url = [m_pluginView URLWithCString:urlData]; + if (!url) + return false; + + if (Frame* frame = core([m_pluginView webFrame])) { + String cookieString = String::fromUTF8(cookiesData, cookiesLength); + if (!cookieString) + return false; + + WebCore::setCookies(frame->document(), url, cookieString); + return true; + } + + return false; +} + +bool NetscapePluginInstanceProxy::getProxy(data_t urlData, mach_msg_type_number_t urlLength, data_t& proxyData, mach_msg_type_number_t& proxyLength) +{ + ASSERT(m_pluginView); + + NSURL *url = [m_pluginView URLWithCString:urlData]; + if (!url) + return false; + + Vector<ProxyServer> proxyServers = proxyServersForURL(url, 0); + WTF::CString proxyStringUTF8 = toString(proxyServers).utf8(); + + proxyLength = proxyStringUTF8.length(); + mig_allocate(reinterpret_cast<vm_address_t*>(&proxyData), proxyLength); + memcpy(proxyData, proxyStringUTF8.data(), proxyLength); + + return true; +} + +bool NetscapePluginInstanceProxy::getAuthenticationInfo(data_t protocolData, data_t hostData, uint32_t port, data_t schemeData, data_t realmData, + data_t& usernameData, mach_msg_type_number_t& usernameLength, data_t& passwordData, mach_msg_type_number_t& passwordLength) +{ + WTF::CString username; + WTF::CString password; + + if (!WebKit::getAuthenticationInfo(protocolData, hostData, port, schemeData, realmData, username, password)) + return false; + + usernameLength = username.length(); + mig_allocate(reinterpret_cast<vm_address_t*>(&usernameData), usernameLength); + memcpy(usernameData, username.data(), usernameLength); + + passwordLength = password.length(); + mig_allocate(reinterpret_cast<vm_address_t*>(&passwordData), passwordLength); + memcpy(passwordData, password.data(), passwordLength); + + return true; +} + +bool NetscapePluginInstanceProxy::convertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace, + double& destX, double& destY, NPCoordinateSpace destSpace) +{ + ASSERT(m_pluginView); + + return [m_pluginView convertFromX:sourceX andY:sourceY space:sourceSpace toX:&destX andY:&destY space:destSpace]; +} + +uint32_t NetscapePluginInstanceProxy::checkIfAllowedToLoadURL(const char* url, const char* target) +{ + uint32_t checkID; + + // Assign a check ID + do { + checkID = ++m_urlCheckCounter; + } while (m_urlChecks.contains(checkID) || !m_urlCheckCounter); + + NSString *frameName = target ? [NSString stringWithCString:target encoding:NSISOLatin1StringEncoding] : nil; + + NSNumber *contextInfo = [[NSNumber alloc] initWithUnsignedInt:checkID]; + WebPluginContainerCheck *check = [WebPluginContainerCheck checkWithRequest:[m_pluginView requestWithURLCString:url] + target:frameName + resultObject:m_pluginView + selector:@selector(_containerCheckResult:contextInfo:) + controller:m_pluginView + contextInfo:contextInfo]; + + [contextInfo release]; + m_urlChecks.set(checkID, check); + [check start]; + + return checkID; +} + +void NetscapePluginInstanceProxy::cancelCheckIfAllowedToLoadURL(uint32_t checkID) +{ + URLCheckMap::iterator it = m_urlChecks.find(checkID); + if (it == m_urlChecks.end()) + return; + + WebPluginContainerCheck *check = it->second.get(); + [check cancel]; + m_urlChecks.remove(it); +} + +void NetscapePluginInstanceProxy::checkIfAllowedToLoadURLResult(uint32_t checkID, bool allowed) +{ + _WKPHCheckIfAllowedToLoadURLResult(m_pluginHostProxy->port(), m_pluginID, checkID, allowed); +} + +void NetscapePluginInstanceProxy::resolveURL(const char* url, const char* target, data_t& resolvedURLData, mach_msg_type_number_t& resolvedURLLength) +{ + ASSERT(m_pluginView); + + WTF::CString resolvedURL = [m_pluginView resolvedURLStringForURL:url target:target]; + + resolvedURLLength = resolvedURL.length(); + mig_allocate(reinterpret_cast<vm_address_t*>(&resolvedURLData), resolvedURLLength); + memcpy(resolvedURLData, resolvedURL.data(), resolvedURLLength); +} + +void NetscapePluginInstanceProxy::privateBrowsingModeDidChange(bool isPrivateBrowsingEnabled) +{ + _WKPHPluginInstancePrivateBrowsingModeDidChange(m_pluginHostProxy->port(), m_pluginID, isPrivateBrowsingEnabled); +} + +static String& globalExceptionString() +{ + DEFINE_STATIC_LOCAL(String, exceptionString, ()); + return exceptionString; +} + +void NetscapePluginInstanceProxy::setGlobalException(const String& exception) +{ + globalExceptionString() = exception; +} + +void NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(ExecState* exec) +{ + if (globalExceptionString().isNull()) + return; + + { + JSLock lock(SilenceAssertionsOnly); + throwError(exec, createError(exec, stringToUString(globalExceptionString()))); + } + + globalExceptionString() = String(); +} + +} // namespace WebKit + +#endif // USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API) |
