1/*
2 * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)
27
28#import "NetscapePluginInstanceProxy.h"
29
30#import "HostedNetscapePluginStream.h"
31#import "NetscapePluginHostProxy.h"
32#import "ProxyInstance.h"
33#import "ProxyRuntimeObject.h"
34#import "WebDataSourceInternal.h"
35#import "WebFrameInternal.h"
36#import "WebHostedNetscapePluginView.h"
37#import "WebKitNSStringExtras.h"
38#import "WebNSDataExtras.h"
39#import "WebNSURLExtras.h"
40#import "WebPluginRequest.h"
41#import "WebUIDelegate.h"
42#import "WebUIDelegatePrivate.h"
43#import "WebViewInternal.h"
44#import <JavaScriptCore/Error.h>
45#import <JavaScriptCore/JSLock.h>
46#import <JavaScriptCore/PropertyNameArray.h>
47#import <WebCore/CookieJar.h>
48#import <WebCore/DocumentLoader.h>
49#import <WebCore/Frame.h>
50#import <WebCore/FrameLoader.h>
51#import <WebCore/FrameTree.h>
52#import <WebCore/KURL.h>
53#import <WebCore/ProxyServer.h>
54#import <WebCore/SecurityOrigin.h>
55#import <WebCore/ScriptController.h>
56#import <WebCore/ScriptValue.h>
57#import <WebCore/StringSourceProvider.h>
58#import <WebCore/npruntime_impl.h>
59#import <WebCore/runtime_object.h>
60#import <WebKitSystemInterface.h>
61#import <mach/mach.h>
62#import <utility>
63#import <wtf/RefCountedLeakCounter.h>
64#import <wtf/text/CString.h>
65
66extern "C" {
67#import "WebKitPluginClientServer.h"
68#import "WebKitPluginHost.h"
69}
70
71using namespace JSC;
72using namespace JSC::Bindings;
73using namespace std;
74using namespace WebCore;
75
76namespace WebKit {
77
78class NetscapePluginInstanceProxy::PluginRequest : public RefCounted<NetscapePluginInstanceProxy::PluginRequest> {
79public:
80    static PassRefPtr<PluginRequest> create(uint32_t requestID, NSURLRequest* request, NSString* frameName, bool allowPopups)
81    {
82        return adoptRef(new PluginRequest(requestID, request, frameName, allowPopups));
83    }
84
85    uint32_t requestID() const { return m_requestID; }
86    NSURLRequest* request() const { return m_request.get(); }
87    NSString* frameName() const { return m_frameName.get(); }
88    bool allowPopups() const { return m_allowPopups; }
89
90private:
91    PluginRequest(uint32_t requestID, NSURLRequest* request, NSString* frameName, bool allowPopups)
92        : m_requestID(requestID)
93        , m_request(request)
94        , m_frameName(frameName)
95        , m_allowPopups(allowPopups)
96    {
97    }
98
99    uint32_t m_requestID;
100    RetainPtr<NSURLRequest*> m_request;
101    RetainPtr<NSString*> m_frameName;
102    bool m_allowPopups;
103};
104
105NetscapePluginInstanceProxy::LocalObjectMap::LocalObjectMap()
106    : m_objectIDCounter(0)
107{
108}
109
110NetscapePluginInstanceProxy::LocalObjectMap::~LocalObjectMap()
111{
112}
113
114inline bool NetscapePluginInstanceProxy::LocalObjectMap::contains(uint32_t objectID) const
115{
116    return m_idToJSObjectMap.contains(objectID);
117}
118
119inline JSC::JSObject* NetscapePluginInstanceProxy::LocalObjectMap::get(uint32_t objectID) const
120{
121    if (objectID == HashTraits<uint32_t>::emptyValue() || HashTraits<uint32_t>::isDeletedValue(objectID))
122        return 0;
123
124    return m_idToJSObjectMap.get(objectID).get();
125}
126
127uint32_t NetscapePluginInstanceProxy::LocalObjectMap::idForObject(JSGlobalData& globalData, JSObject* object)
128{
129    // This method creates objects with refcount of 1, but doesn't increase refcount when returning
130    // found objects. This extra count accounts for the main "reference" kept by plugin process.
131
132    // To avoid excessive IPC, plugin process doesn't send each NPObject release/retain call to
133    // Safari. It only sends one when the last reference is removed, and it can destroy the proxy
134    // NPObject.
135
136    // However, the browser may be sending the same object out to plug-in as a function call
137    // argument at the same time - neither side can know what the other one is doing. So,
138    // is to make PCForgetBrowserObject call return a boolean result, making it possible for
139    // the browser to make plugin host keep the proxy with zero refcount for a little longer.
140
141    uint32_t objectID = 0;
142
143    HashMap<JSC::JSObject*, pair<uint32_t, uint32_t> >::iterator iter = m_jsObjectToIDMap.find(object);
144    if (iter != m_jsObjectToIDMap.end())
145        return iter->second.first;
146
147    do {
148        objectID = ++m_objectIDCounter;
149    } while (!m_objectIDCounter || m_objectIDCounter == static_cast<uint32_t>(-1) || m_idToJSObjectMap.contains(objectID));
150
151    m_idToJSObjectMap.set(objectID, Strong<JSObject>(globalData, object));
152    m_jsObjectToIDMap.set(object, make_pair<uint32_t, uint32_t>(objectID, 1));
153
154    return objectID;
155}
156
157void NetscapePluginInstanceProxy::LocalObjectMap::retain(JSC::JSObject* object)
158{
159    HashMap<JSC::JSObject*, pair<uint32_t, uint32_t> >::iterator iter = m_jsObjectToIDMap.find(object);
160    ASSERT(iter != m_jsObjectToIDMap.end());
161
162    iter->second.second = iter->second.second + 1;
163}
164
165void NetscapePluginInstanceProxy::LocalObjectMap::release(JSC::JSObject* object)
166{
167    HashMap<JSC::JSObject*, pair<uint32_t, uint32_t> >::iterator iter = m_jsObjectToIDMap.find(object);
168    ASSERT(iter != m_jsObjectToIDMap.end());
169
170    ASSERT(iter->second.second > 0);
171    iter->second.second = iter->second.second - 1;
172    if (!iter->second.second) {
173        m_idToJSObjectMap.remove(iter->second.first);
174        m_jsObjectToIDMap.remove(iter);
175    }
176}
177
178void NetscapePluginInstanceProxy::LocalObjectMap::clear()
179{
180    m_idToJSObjectMap.clear();
181    m_jsObjectToIDMap.clear();
182}
183
184bool NetscapePluginInstanceProxy::LocalObjectMap::forget(uint32_t objectID)
185{
186    if (objectID == HashTraits<uint32_t>::emptyValue() || HashTraits<uint32_t>::isDeletedValue(objectID)) {
187        LOG_ERROR("NetscapePluginInstanceProxy::LocalObjectMap::forget: local object id %u is not valid.", objectID);
188        return true;
189    }
190
191    HashMap<uint32_t, JSC::Strong<JSC::JSObject> >::iterator iter = m_idToJSObjectMap.find(objectID);
192    if (iter == m_idToJSObjectMap.end()) {
193        LOG_ERROR("NetscapePluginInstanceProxy::LocalObjectMap::forget: local object %u doesn't exist.", objectID);
194        return true;
195    }
196
197    HashMap<JSC::JSObject*, pair<uint32_t, uint32_t> >::iterator rIter = m_jsObjectToIDMap.find(iter->second.get());
198
199    // If the object is being sent to plug-in right now, then it's not the time to forget.
200    if (rIter->second.second != 1)
201        return false;
202
203    m_jsObjectToIDMap.remove(rIter);
204    m_idToJSObjectMap.remove(iter);
205    return true;
206}
207
208static uint32_t pluginIDCounter;
209
210bool NetscapePluginInstanceProxy::m_inDestroy;
211
212#ifndef NDEBUG
213static WTF::RefCountedLeakCounter netscapePluginInstanceProxyCounter("NetscapePluginInstanceProxy");
214#endif
215
216NetscapePluginInstanceProxy::NetscapePluginInstanceProxy(NetscapePluginHostProxy* pluginHostProxy, WebHostedNetscapePluginView *pluginView, bool fullFramePlugin)
217    : m_pluginHostProxy(pluginHostProxy)
218    , m_pluginView(pluginView)
219    , m_requestTimer(this, &NetscapePluginInstanceProxy::requestTimerFired)
220    , m_currentURLRequestID(0)
221    , m_renderContextID(0)
222    , m_rendererType(UseSoftwareRenderer)
223    , m_waitingForReply(false)
224    , m_urlCheckCounter(0)
225    , m_pluginFunctionCallDepth(0)
226    , m_shouldStopSoon(false)
227    , m_currentRequestID(0)
228    , m_pluginIsWaitingForDraw(false)
229{
230    ASSERT(m_pluginView);
231
232    if (fullFramePlugin) {
233        // For full frame plug-ins, the first requestID will always be the one for the already
234        // open stream.
235        ++m_currentURLRequestID;
236    }
237
238    // Assign a plug-in ID.
239    do {
240        m_pluginID = ++pluginIDCounter;
241    } while (pluginHostProxy->pluginInstance(m_pluginID) || !m_pluginID);
242
243#ifndef NDEBUG
244    netscapePluginInstanceProxyCounter.increment();
245#endif
246}
247
248PassRefPtr<NetscapePluginInstanceProxy> NetscapePluginInstanceProxy::create(NetscapePluginHostProxy* pluginHostProxy, WebHostedNetscapePluginView *pluginView, bool fullFramePlugin)
249{
250    RefPtr<NetscapePluginInstanceProxy> proxy = adoptRef(new NetscapePluginInstanceProxy(pluginHostProxy, pluginView, fullFramePlugin));
251    pluginHostProxy->addPluginInstance(proxy.get());
252    return proxy.release();
253}
254
255NetscapePluginInstanceProxy::~NetscapePluginInstanceProxy()
256{
257    ASSERT(!m_pluginHostProxy);
258
259    m_pluginID = 0;
260    deleteAllValues(m_replies);
261
262#ifndef NDEBUG
263    netscapePluginInstanceProxyCounter.decrement();
264#endif
265}
266
267void NetscapePluginInstanceProxy::resize(NSRect size, NSRect clipRect)
268{
269    uint32_t requestID = 0;
270
271    requestID = nextRequestID();
272
273    _WKPHResizePluginInstance(m_pluginHostProxy->port(), m_pluginID, requestID,
274                              size.origin.x, size.origin.y, size.size.width, size.size.height,
275                              clipRect.origin.x, clipRect.origin.y, clipRect.size.width, clipRect.size.height);
276
277    waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
278}
279
280void NetscapePluginInstanceProxy::stopAllStreams()
281{
282    Vector<RefPtr<HostedNetscapePluginStream> > streamsCopy;
283    copyValuesToVector(m_streams, streamsCopy);
284    for (size_t i = 0; i < streamsCopy.size(); i++)
285        streamsCopy[i]->stop();
286}
287
288void NetscapePluginInstanceProxy::cleanup()
289{
290    stopAllStreams();
291
292    m_requestTimer.stop();
293
294    // Clear the object map, this will cause any outstanding JS objects that the plug-in had a reference to
295    // to go away when the next garbage collection takes place.
296    m_localObjects.clear();
297
298    if (Frame* frame = core([m_pluginView webFrame]))
299        frame->script()->cleanupScriptObjectsForPlugin(m_pluginView);
300
301    ProxyInstanceSet instances;
302    instances.swap(m_instances);
303
304    // Invalidate all proxy instances.
305    ProxyInstanceSet::const_iterator end = instances.end();
306    for (ProxyInstanceSet::const_iterator it = instances.begin(); it != end; ++it)
307        (*it)->invalidate();
308
309    m_pluginView = nil;
310    m_manualStream = 0;
311}
312
313void NetscapePluginInstanceProxy::invalidate()
314{
315    // If the plug-in host has died, the proxy will be null.
316    if (!m_pluginHostProxy)
317        return;
318
319    m_pluginHostProxy->removePluginInstance(this);
320    m_pluginHostProxy = 0;
321}
322
323void NetscapePluginInstanceProxy::destroy()
324{
325    uint32_t requestID = nextRequestID();
326
327    ASSERT(!m_inDestroy);
328    m_inDestroy = true;
329
330    FrameLoadMap::iterator end = m_pendingFrameLoads.end();
331    for (FrameLoadMap::iterator it = m_pendingFrameLoads.begin(); it != end; ++it)
332        [(it->first) _setInternalLoadDelegate:nil];
333
334    _WKPHDestroyPluginInstance(m_pluginHostProxy->port(), m_pluginID, requestID);
335
336    // If the plug-in host crashes while we're waiting for a reply, the last reference to the instance proxy
337    // will go away. Prevent this by protecting it here.
338    RefPtr<NetscapePluginInstanceProxy> protect(this);
339
340    // We don't care about the reply here - we just want to block until the plug-in instance has been torn down.
341    waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
342
343    m_inDestroy = false;
344
345    cleanup();
346    invalidate();
347}
348
349void NetscapePluginInstanceProxy::setManualStream(PassRefPtr<HostedNetscapePluginStream> manualStream)
350{
351    ASSERT(!m_manualStream);
352
353    m_manualStream = manualStream;
354}
355
356bool NetscapePluginInstanceProxy::cancelStreamLoad(uint32_t streamID, NPReason reason)
357{
358    HostedNetscapePluginStream* stream = 0;
359
360    if (m_manualStream && streamID == 1)
361        stream = m_manualStream.get();
362    else
363        stream = m_streams.get(streamID).get();
364
365    if (!stream)
366        return false;
367
368    stream->cancelLoad(reason);
369    return true;
370}
371
372void NetscapePluginInstanceProxy::disconnectStream(HostedNetscapePluginStream* stream)
373{
374    if (stream == m_manualStream) {
375        m_manualStream = 0;
376        return;
377    }
378
379    ASSERT(m_streams.get(stream->streamID()) == stream);
380    m_streams.remove(stream->streamID());
381}
382
383void NetscapePluginInstanceProxy::pluginHostDied()
384{
385    m_pluginHostProxy = 0;
386
387    [m_pluginView pluginHostDied];
388
389    cleanup();
390}
391
392void NetscapePluginInstanceProxy::focusChanged(bool hasFocus)
393{
394    _WKPHPluginInstanceFocusChanged(m_pluginHostProxy->port(), m_pluginID, hasFocus);
395}
396
397void NetscapePluginInstanceProxy::windowFocusChanged(bool hasFocus)
398{
399    _WKPHPluginInstanceWindowFocusChanged(m_pluginHostProxy->port(), m_pluginID, hasFocus);
400}
401
402void NetscapePluginInstanceProxy::windowFrameChanged(NSRect frame)
403{
404    _WKPHPluginInstanceWindowFrameChanged(m_pluginHostProxy->port(), m_pluginID, frame.origin.x, frame.origin.y, frame.size.width, frame.size.height,
405                                          NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]));
406}
407
408void NetscapePluginInstanceProxy::startTimers(bool throttleTimers)
409{
410    _WKPHPluginInstanceStartTimers(m_pluginHostProxy->port(), m_pluginID, throttleTimers);
411}
412
413void NetscapePluginInstanceProxy::mouseEvent(NSView *pluginView, NSEvent *event, NPCocoaEventType type)
414{
415    NSPoint screenPoint = [[event window] convertBaseToScreen:[event locationInWindow]];
416    NSPoint pluginPoint = [pluginView convertPoint:[event locationInWindow] fromView:nil];
417
418    int clickCount;
419    if (type == NPCocoaEventMouseEntered || type == NPCocoaEventMouseExited)
420        clickCount = 0;
421    else
422        clickCount = [event clickCount];
423
424
425    _WKPHPluginInstanceMouseEvent(m_pluginHostProxy->port(), m_pluginID,
426                                  [event timestamp],
427                                  type, [event modifierFlags],
428                                  pluginPoint.x, pluginPoint.y,
429                                  screenPoint.x, screenPoint.y,
430                                  NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]),
431                                  [event buttonNumber], clickCount,
432                                  [event deltaX], [event deltaY], [event deltaZ]);
433}
434
435void NetscapePluginInstanceProxy::keyEvent(NSView *pluginView, NSEvent *event, NPCocoaEventType type)
436{
437    NSData *charactersData = [[event characters] dataUsingEncoding:NSUTF8StringEncoding];
438    NSData *charactersIgnoringModifiersData = [[event charactersIgnoringModifiers] dataUsingEncoding:NSUTF8StringEncoding];
439
440    _WKPHPluginInstanceKeyboardEvent(m_pluginHostProxy->port(), m_pluginID,
441                                     [event timestamp],
442                                     type, [event modifierFlags],
443                                     const_cast<char*>(reinterpret_cast<const char*>([charactersData bytes])), [charactersData length],
444                                     const_cast<char*>(reinterpret_cast<const char*>([charactersIgnoringModifiersData bytes])), [charactersIgnoringModifiersData length],
445                                     [event isARepeat], [event keyCode], WKGetNSEventKeyChar(event));
446}
447
448void NetscapePluginInstanceProxy::syntheticKeyDownWithCommandModifier(int keyCode, char character)
449{
450    NSData *charactersData = [NSData dataWithBytes:&character length:1];
451
452    _WKPHPluginInstanceKeyboardEvent(m_pluginHostProxy->port(), m_pluginID,
453                                     [NSDate timeIntervalSinceReferenceDate],
454                                     NPCocoaEventKeyDown, NSCommandKeyMask,
455                                     const_cast<char*>(reinterpret_cast<const char*>([charactersData bytes])), [charactersData length],
456                                     const_cast<char*>(reinterpret_cast<const char*>([charactersData bytes])), [charactersData length],
457                                     false, keyCode, character);
458}
459
460void NetscapePluginInstanceProxy::flagsChanged(NSEvent *event)
461{
462    _WKPHPluginInstanceKeyboardEvent(m_pluginHostProxy->port(), m_pluginID,
463                                     [event timestamp], NPCocoaEventFlagsChanged,
464                                     [event modifierFlags], 0, 0, 0, 0, false, [event keyCode], 0);
465}
466
467void NetscapePluginInstanceProxy::insertText(NSString *text)
468{
469    NSData *textData = [text dataUsingEncoding:NSUTF8StringEncoding];
470
471    _WKPHPluginInstanceInsertText(m_pluginHostProxy->port(), m_pluginID,
472                                  const_cast<char*>(reinterpret_cast<const char*>([textData bytes])), [textData length]);
473}
474
475bool NetscapePluginInstanceProxy::wheelEvent(NSView *pluginView, NSEvent *event)
476{
477    NSPoint pluginPoint = [pluginView convertPoint:[event locationInWindow] fromView:nil];
478
479    uint32_t requestID = nextRequestID();
480    _WKPHPluginInstanceWheelEvent(m_pluginHostProxy->port(), m_pluginID, requestID,
481                                  [event timestamp], [event modifierFlags],
482                                  pluginPoint.x, pluginPoint.y, [event buttonNumber],
483                                  [event deltaX], [event deltaY], [event deltaZ]);
484
485    auto_ptr<NetscapePluginInstanceProxy::BooleanReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanReply>(requestID);
486    if (!reply.get() || !reply->m_result)
487        return false;
488
489    return true;
490}
491
492void NetscapePluginInstanceProxy::print(CGContextRef context, unsigned width, unsigned height)
493{
494    uint32_t requestID = nextRequestID();
495    _WKPHPluginInstancePrint(m_pluginHostProxy->port(), m_pluginID, requestID, width, height);
496
497    auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID);
498    if (!reply.get() || !reply->m_returnValue)
499        return;
500
501    RetainPtr<CGDataProvider> dataProvider(AdoptCF, CGDataProviderCreateWithCFData(reply->m_result.get()));
502    RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB());
503    RetainPtr<CGImageRef> image(AdoptCF, CGImageCreate(width, height, 8, 32, width * 4, colorSpace.get(), kCGImageAlphaFirst, dataProvider.get(), 0, false, kCGRenderingIntentDefault));
504
505    // Flip the context and draw the image.
506    CGContextSaveGState(context);
507    CGContextTranslateCTM(context, 0.0, height);
508    CGContextScaleCTM(context, 1.0, -1.0);
509
510    CGContextDrawImage(context, CGRectMake(0, 0, width, height), image.get());
511
512    CGContextRestoreGState(context);
513}
514
515void NetscapePluginInstanceProxy::snapshot(CGContextRef context, unsigned width, unsigned height)
516{
517    uint32_t requestID = nextRequestID();
518    _WKPHPluginInstanceSnapshot(m_pluginHostProxy->port(), m_pluginID, requestID, width, height);
519
520    auto_ptr<NetscapePluginInstanceProxy::BooleanAndDataReply> reply = waitForReply<NetscapePluginInstanceProxy::BooleanAndDataReply>(requestID);
521    if (!reply.get() || !reply->m_returnValue)
522        return;
523
524    RetainPtr<CGDataProvider> dataProvider(AdoptCF, CGDataProviderCreateWithCFData(reply->m_result.get()));
525    RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB());
526    RetainPtr<CGImageRef> image(AdoptCF, CGImageCreate(width, height, 8, 32, width * 4, colorSpace.get(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, dataProvider.get(), 0, false, kCGRenderingIntentDefault));
527
528    CGContextDrawImage(context, CGRectMake(0, 0, width, height), image.get());
529}
530
531void NetscapePluginInstanceProxy::stopTimers()
532{
533    _WKPHPluginInstanceStopTimers(m_pluginHostProxy->port(), m_pluginID);
534}
535
536void NetscapePluginInstanceProxy::status(const char* message)
537{
538    RetainPtr<CFStringRef> status(AdoptCF, CFStringCreateWithCString(0, message ? message : "", kCFStringEncodingUTF8));
539    if (!status)
540        return;
541
542    WebView *wv = [m_pluginView webView];
543    [[wv _UIDelegateForwarder] webView:wv setStatusText:(NSString *)status.get()];
544}
545
546NPError NetscapePluginInstanceProxy::loadURL(const char* url, const char* target, const char* postData, uint32_t postLen, LoadURLFlags flags, uint32_t& streamID)
547{
548    if (!url)
549        return NPERR_INVALID_PARAM;
550
551    NSMutableURLRequest *request = [m_pluginView requestWithURLCString:url];
552
553    if (flags & IsPost) {
554        NSData *httpBody = nil;
555
556        if (flags & PostDataIsFile) {
557            // If we're posting a file, buf is either a file URL or a path to the file.
558            if (!postData)
559                return NPERR_INVALID_PARAM;
560            RetainPtr<CFStringRef> bufString(AdoptCF, CFStringCreateWithCString(kCFAllocatorDefault, postData, kCFStringEncodingWindowsLatin1));
561            if (!bufString)
562                return NPERR_INVALID_PARAM;
563
564            NSURL *fileURL = [NSURL _web_URLWithDataAsString:(NSString *)bufString.get()];
565            NSString *path;
566            if ([fileURL isFileURL])
567                path = [fileURL path];
568            else
569                path = (NSString *)bufString.get();
570            httpBody = [NSData dataWithContentsOfFile:[path _webkit_fixedCarbonPOSIXPath]];
571            if (!httpBody)
572                return NPERR_FILE_NOT_FOUND;
573        } else
574            httpBody = [NSData dataWithBytes:postData length:postLen];
575
576        if (![httpBody length])
577            return NPERR_INVALID_PARAM;
578
579        [request setHTTPMethod:@"POST"];
580
581        if (flags & AllowHeadersInPostData) {
582            if ([httpBody _web_startsWithBlankLine])
583                httpBody = [httpBody subdataWithRange:NSMakeRange(1, [httpBody length] - 1)];
584            else {
585                NSInteger location = [httpBody _web_locationAfterFirstBlankLine];
586                if (location != NSNotFound) {
587                    // If the blank line is somewhere in the middle of postData, everything before is the header.
588                    NSData *headerData = [httpBody subdataWithRange:NSMakeRange(0, location)];
589                    NSMutableDictionary *header = [headerData _webkit_parseRFC822HeaderFields];
590                    unsigned dataLength = [httpBody length] - location;
591
592                    // Sometimes plugins like to set Content-Length themselves when they post,
593                    // but CFNetwork does not like that. So we will remove the header
594                    // and instead truncate the data to the requested length.
595                    NSString *contentLength = [header objectForKey:@"Content-Length"];
596
597                    if (contentLength)
598                        dataLength = min(static_cast<unsigned>([contentLength intValue]), dataLength);
599                    [header removeObjectForKey:@"Content-Length"];
600
601                    if ([header count] > 0)
602                        [request setAllHTTPHeaderFields:header];
603
604                    // Everything after the blank line is the actual content of the POST.
605                    httpBody = [httpBody subdataWithRange:NSMakeRange(location, dataLength)];
606                }
607            }
608        }
609
610        if (![httpBody length])
611            return NPERR_INVALID_PARAM;
612
613        // Plug-ins expect to receive uncached data when doing a POST (3347134).
614        [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
615        [request setHTTPBody:httpBody];
616    }
617
618    return loadRequest(request, target, flags & AllowPopups, streamID);
619}
620
621void NetscapePluginInstanceProxy::performRequest(PluginRequest* pluginRequest)
622{
623    ASSERT(m_pluginView);
624
625    NSURLRequest *request = pluginRequest->request();
626    NSString *frameName = pluginRequest->frameName();
627    WebFrame *frame = nil;
628
629    NSURL *URL = [request URL];
630    NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
631
632    ASSERT(frameName || JSString);
633    if (frameName) {
634        // FIXME - need to get rid of this window creation which
635        // bypasses normal targeted link handling
636        frame = kit(core([m_pluginView webFrame])->loader()->findFrameForNavigation(frameName));
637        if (!frame) {
638            WebView *currentWebView = [m_pluginView webView];
639            NSDictionary *features = [[NSDictionary alloc] init];
640            WebView *newWebView = [[currentWebView _UIDelegateForwarder] webView:currentWebView
641                                                        createWebViewWithRequest:nil
642                                                                  windowFeatures:features];
643            [features release];
644
645            if (!newWebView) {
646                _WKPHLoadURLNotify(m_pluginHostProxy->port(), m_pluginID, pluginRequest->requestID(), NPERR_GENERIC_ERROR);
647                return;
648            }
649
650            frame = [newWebView mainFrame];
651            core(frame)->tree()->setName(frameName);
652            [[newWebView _UIDelegateForwarder] webViewShow:newWebView];
653        }
654    }
655
656    if (JSString) {
657        ASSERT(!frame || [m_pluginView webFrame] == frame);
658        evaluateJavaScript(pluginRequest);
659    } else {
660        [frame loadRequest:request];
661
662        // Check if another plug-in view or even this view is waiting for the frame to load.
663        // If it is, tell it that the load was cancelled because it will be anyway.
664        WebHostedNetscapePluginView *view = [frame _internalLoadDelegate];
665        if (view != nil) {
666            ASSERT([view isKindOfClass:[WebHostedNetscapePluginView class]]);
667            [view webFrame:frame didFinishLoadWithReason:NPRES_USER_BREAK];
668        }
669        m_pendingFrameLoads.set(frame, pluginRequest);
670        [frame _setInternalLoadDelegate:m_pluginView];
671    }
672
673}
674
675void NetscapePluginInstanceProxy::webFrameDidFinishLoadWithReason(WebFrame* webFrame, NPReason reason)
676{
677    FrameLoadMap::iterator it = m_pendingFrameLoads.find(webFrame);
678    ASSERT(it != m_pendingFrameLoads.end());
679
680    PluginRequest* pluginRequest = it->second.get();
681    _WKPHLoadURLNotify(m_pluginHostProxy->port(), m_pluginID, pluginRequest->requestID(), reason);
682
683    m_pendingFrameLoads.remove(it);
684
685    [webFrame _setInternalLoadDelegate:nil];
686}
687
688void NetscapePluginInstanceProxy::evaluateJavaScript(PluginRequest* pluginRequest)
689{
690    NSURL *URL = [pluginRequest->request() URL];
691    NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
692    ASSERT(JSString);
693
694    NSString *result = [[m_pluginView webFrame] _stringByEvaluatingJavaScriptFromString:JSString forceUserGesture:pluginRequest->allowPopups()];
695
696    // Don't continue if stringByEvaluatingJavaScriptFromString caused the plug-in to stop.
697    if (!m_pluginHostProxy)
698        return;
699
700    if (pluginRequest->frameName() != nil)
701        return;
702
703    if ([result length] > 0) {
704        // Don't call NPP_NewStream and other stream methods if there is no JS result to deliver. This is what Mozilla does.
705        NSData *JSData = [result dataUsingEncoding:NSUTF8StringEncoding];
706
707        RefPtr<HostedNetscapePluginStream> stream = HostedNetscapePluginStream::create(this, pluginRequest->requestID(), pluginRequest->request());
708        m_streams.add(stream->streamID(), stream);
709
710        RetainPtr<NSURLResponse> response(AdoptNS, [[NSURLResponse alloc] initWithURL:URL
711                                                                             MIMEType:@"text/plain"
712                                                                expectedContentLength:[JSData length]
713                                                                     textEncodingName:nil]);
714        stream->startStreamWithResponse(response.get());
715        stream->didReceiveData(0, static_cast<const char*>([JSData bytes]), [JSData length]);
716        stream->didFinishLoading(0);
717    }
718}
719
720void NetscapePluginInstanceProxy::requestTimerFired(Timer<NetscapePluginInstanceProxy>*)
721{
722    ASSERT(!m_pluginRequests.isEmpty());
723    ASSERT(m_pluginView);
724
725    RefPtr<PluginRequest> request = m_pluginRequests.first();
726    m_pluginRequests.removeFirst();
727
728    if (!m_pluginRequests.isEmpty())
729        m_requestTimer.startOneShot(0);
730
731    performRequest(request.get());
732}
733
734NPError NetscapePluginInstanceProxy::loadRequest(NSURLRequest *request, const char* cTarget, bool allowPopups, uint32_t& requestID)
735{
736    NSURL *URL = [request URL];
737
738    if (!URL)
739        return NPERR_INVALID_URL;
740
741    // Don't allow requests to be loaded when the document loader is stopping all loaders.
742    DocumentLoader* documentLoader = [[m_pluginView dataSource] _documentLoader];
743    if (!documentLoader || documentLoader->isStopping())
744        return NPERR_GENERIC_ERROR;
745
746    NSString *target = nil;
747    if (cTarget) {
748        // Find the frame given the target string.
749        target = [NSString stringWithCString:cTarget encoding:NSISOLatin1StringEncoding];
750    }
751    WebFrame *frame = [m_pluginView webFrame];
752
753    // don't let a plugin start any loads if it is no longer part of a document that is being
754    // displayed unless the loads are in the same frame as the plugin.
755    if (documentLoader != core([m_pluginView webFrame])->loader()->activeDocumentLoader() &&
756        (!cTarget || [frame findFrameNamed:target] != frame)) {
757        return NPERR_GENERIC_ERROR;
758    }
759
760    NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
761    if (JSString != nil) {
762        if (![[[m_pluginView webView] preferences] isJavaScriptEnabled]) {
763            // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does.
764            return NPERR_GENERIC_ERROR;
765        }
766    } else {
767        if (!core([m_pluginView webFrame])->document()->securityOrigin()->canDisplay(URL))
768            return NPERR_GENERIC_ERROR;
769    }
770
771    // FIXME: Handle wraparound
772    requestID = ++m_currentURLRequestID;
773
774    if (cTarget || JSString) {
775        // Make when targetting a frame or evaluating a JS string, perform the request after a delay because we don't
776        // want to potentially kill the plug-in inside of its URL request.
777
778        if (JSString && target && [frame findFrameNamed:target] != frame) {
779            // For security reasons, only allow JS requests to be made on the frame that contains the plug-in.
780            return NPERR_INVALID_PARAM;
781        }
782
783        RefPtr<PluginRequest> pluginRequest = PluginRequest::create(requestID, request, target, allowPopups);
784        m_pluginRequests.append(pluginRequest.release());
785        m_requestTimer.startOneShot(0);
786    } else {
787        RefPtr<HostedNetscapePluginStream> stream = HostedNetscapePluginStream::create(this, requestID, request);
788
789        ASSERT(!m_streams.contains(requestID));
790        m_streams.add(requestID, stream);
791        stream->start();
792    }
793
794    return NPERR_NO_ERROR;
795}
796
797NetscapePluginInstanceProxy::Reply* NetscapePluginInstanceProxy::processRequestsAndWaitForReply(uint32_t requestID)
798{
799    Reply* reply = 0;
800
801    ASSERT(m_pluginHostProxy);
802    while (!(reply = m_replies.take(requestID))) {
803        if (!m_pluginHostProxy->processRequests())
804            return 0;
805
806        // The host proxy can be destroyed while executing a nested processRequests() call, in which case it's normal
807        // to get a success result, but be unable to keep looping.
808        if (!m_pluginHostProxy)
809            return 0;
810    }
811
812    ASSERT(reply);
813    return reply;
814}
815
816// NPRuntime support
817bool NetscapePluginInstanceProxy::getWindowNPObject(uint32_t& objectID)
818{
819    Frame* frame = core([m_pluginView webFrame]);
820    if (!frame)
821        return false;
822
823    if (!frame->script()->canExecuteScripts(NotAboutToExecuteScript))
824        objectID = 0;
825    else
826        objectID = m_localObjects.idForObject(*pluginWorld()->globalData(), frame->script()->windowShell(pluginWorld())->window());
827
828    return true;
829}
830
831bool NetscapePluginInstanceProxy::getPluginElementNPObject(uint32_t& objectID)
832{
833    Frame* frame = core([m_pluginView webFrame]);
834    if (!frame)
835        return false;
836
837    if (JSObject* object = frame->script()->jsObjectForPluginElement([m_pluginView element]))
838        objectID = m_localObjects.idForObject(*pluginWorld()->globalData(), object);
839    else
840        objectID = 0;
841
842    return true;
843}
844
845bool NetscapePluginInstanceProxy::forgetBrowserObjectID(uint32_t objectID)
846{
847    return m_localObjects.forget(objectID);
848}
849
850bool NetscapePluginInstanceProxy::evaluate(uint32_t objectID, const String& script, data_t& resultData, mach_msg_type_number_t& resultLength, bool allowPopups)
851{
852    resultData = 0;
853    resultLength = 0;
854
855    if (m_inDestroy)
856        return false;
857
858    if (!m_localObjects.contains(objectID)) {
859        LOG_ERROR("NetscapePluginInstanceProxy::evaluate: local object %u doesn't exist.", objectID);
860        return false;
861    }
862
863    Frame* frame = core([m_pluginView webFrame]);
864    if (!frame)
865        return false;
866
867    JSLock lock(SilenceAssertionsOnly);
868
869    Strong<JSGlobalObject> globalObject(*pluginWorld()->globalData(), frame->script()->globalObject(pluginWorld()));
870    ExecState* exec = globalObject->globalExec();
871
872    bool oldAllowPopups = frame->script()->allowPopupsFromPlugin();
873    frame->script()->setAllowPopupsFromPlugin(allowPopups);
874
875    globalObject->globalData().timeoutChecker.start();
876    Completion completion = JSC::evaluate(exec, globalObject->globalScopeChain(), makeSource(script));
877    globalObject->globalData().timeoutChecker.stop();
878    ComplType type = completion.complType();
879
880    frame->script()->setAllowPopupsFromPlugin(oldAllowPopups);
881
882    JSValue result;
883    if (type == Normal)
884        result = completion.value();
885
886    if (!result)
887        result = jsUndefined();
888
889    marshalValue(exec, result, resultData, resultLength);
890    exec->clearException();
891    return true;
892}
893
894bool 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)
895{
896    resultData = 0;
897    resultLength = 0;
898
899    if (m_inDestroy)
900        return false;
901
902    JSObject* object = m_localObjects.get(objectID);
903    if (!object) {
904        LOG_ERROR("NetscapePluginInstanceProxy::invoke: local object %u doesn't exist.", objectID);
905        return false;
906    }
907
908    Frame* frame = core([m_pluginView webFrame]);
909    if (!frame)
910        return false;
911
912    ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec();
913    JSLock lock(SilenceAssertionsOnly);
914    JSValue function = object->get(exec, methodName);
915    CallData callData;
916    CallType callType = getCallData(function, callData);
917    if (callType == CallTypeNone)
918        return false;
919
920    MarkedArgumentBuffer argList;
921    demarshalValues(exec, argumentsData, argumentsLength, argList);
922
923    RefPtr<JSGlobalData> globalData = pluginWorld()->globalData();
924    globalData->timeoutChecker.start();
925    JSValue value = call(exec, function, callType, callData, object, argList);
926    globalData->timeoutChecker.stop();
927
928    marshalValue(exec, value, resultData, resultLength);
929    exec->clearException();
930    return true;
931}
932
933bool NetscapePluginInstanceProxy::invokeDefault(uint32_t objectID, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength)
934{
935    if (m_inDestroy)
936        return false;
937
938    JSObject* object = m_localObjects.get(objectID);
939    if (!object) {
940        LOG_ERROR("NetscapePluginInstanceProxy::invokeDefault: local object %u doesn't exist.", objectID);
941        return false;
942    }
943
944    Frame* frame = core([m_pluginView webFrame]);
945    if (!frame)
946        return false;
947
948    ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec();
949    JSLock lock(SilenceAssertionsOnly);
950    CallData callData;
951    CallType callType = object->getCallData(callData);
952    if (callType == CallTypeNone)
953        return false;
954
955    MarkedArgumentBuffer argList;
956    demarshalValues(exec, argumentsData, argumentsLength, argList);
957
958    RefPtr<JSGlobalData> globalData = pluginWorld()->globalData();
959    globalData->timeoutChecker.start();
960    JSValue value = call(exec, object, callType, callData, object, argList);
961    globalData->timeoutChecker.stop();
962
963    marshalValue(exec, value, resultData, resultLength);
964    exec->clearException();
965    return true;
966}
967
968bool NetscapePluginInstanceProxy::construct(uint32_t objectID, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength)
969{
970    if (m_inDestroy)
971        return false;
972
973    JSObject* object = m_localObjects.get(objectID);
974    if (!object) {
975        LOG_ERROR("NetscapePluginInstanceProxy::construct: local object %u doesn't exist.", objectID);
976        return false;
977    }
978
979    Frame* frame = core([m_pluginView webFrame]);
980    if (!frame)
981        return false;
982
983    ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec();
984    JSLock lock(SilenceAssertionsOnly);
985
986    ConstructData constructData;
987    ConstructType constructType = object->getConstructData(constructData);
988    if (constructType == ConstructTypeNone)
989        return false;
990
991    MarkedArgumentBuffer argList;
992    demarshalValues(exec, argumentsData, argumentsLength, argList);
993
994    RefPtr<JSGlobalData> globalData = pluginWorld()->globalData();
995    globalData->timeoutChecker.start();
996    JSValue value = JSC::construct(exec, object, constructType, constructData, argList);
997    globalData->timeoutChecker.stop();
998
999    marshalValue(exec, value, resultData, resultLength);
1000    exec->clearException();
1001    return true;
1002}
1003
1004bool NetscapePluginInstanceProxy::getProperty(uint32_t objectID, const Identifier& propertyName, data_t& resultData, mach_msg_type_number_t& resultLength)
1005{
1006    if (m_inDestroy)
1007        return false;
1008
1009    JSObject* object = m_localObjects.get(objectID);
1010    if (!object) {
1011        LOG_ERROR("NetscapePluginInstanceProxy::getProperty: local object %u doesn't exist.", objectID);
1012        return false;
1013    }
1014
1015    Frame* frame = core([m_pluginView webFrame]);
1016    if (!frame)
1017        return false;
1018
1019    ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec();
1020    JSLock lock(SilenceAssertionsOnly);
1021    JSValue value = object->get(exec, propertyName);
1022
1023    marshalValue(exec, value, resultData, resultLength);
1024    exec->clearException();
1025    return true;
1026}
1027
1028bool NetscapePluginInstanceProxy::getProperty(uint32_t objectID, unsigned propertyName, data_t& resultData, mach_msg_type_number_t& resultLength)
1029{
1030    JSObject* object = m_localObjects.get(objectID);
1031    if (!object) {
1032        LOG_ERROR("NetscapePluginInstanceProxy::getProperty: local object %u doesn't exist.", objectID);
1033        return false;
1034    }
1035
1036    Frame* frame = core([m_pluginView webFrame]);
1037    if (!frame)
1038        return false;
1039
1040    ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec();
1041    JSLock lock(SilenceAssertionsOnly);
1042    JSValue value = object->get(exec, propertyName);
1043
1044    marshalValue(exec, value, resultData, resultLength);
1045    exec->clearException();
1046    return true;
1047}
1048
1049bool NetscapePluginInstanceProxy::setProperty(uint32_t objectID, const Identifier& propertyName, data_t valueData, mach_msg_type_number_t valueLength)
1050{
1051    if (m_inDestroy)
1052        return false;
1053
1054    JSObject* object = m_localObjects.get(objectID);
1055    if (!object) {
1056        LOG_ERROR("NetscapePluginInstanceProxy::setProperty: local object %u doesn't exist.", objectID);
1057        return false;
1058    }
1059
1060    Frame* frame = core([m_pluginView webFrame]);
1061    if (!frame)
1062        return false;
1063
1064    ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec();
1065    JSLock lock(SilenceAssertionsOnly);
1066
1067    JSValue value = demarshalValue(exec, valueData, valueLength);
1068    PutPropertySlot slot;
1069    object->put(exec, propertyName, value, slot);
1070
1071    exec->clearException();
1072    return true;
1073}
1074
1075bool NetscapePluginInstanceProxy::setProperty(uint32_t objectID, unsigned propertyName, data_t valueData, mach_msg_type_number_t valueLength)
1076{
1077    if (m_inDestroy)
1078        return false;
1079
1080    JSObject* object = m_localObjects.get(objectID);
1081    if (!object) {
1082        LOG_ERROR("NetscapePluginInstanceProxy::setProperty: local object %u doesn't exist.", objectID);
1083        return false;
1084    }
1085
1086    Frame* frame = core([m_pluginView webFrame]);
1087    if (!frame)
1088        return false;
1089
1090    ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec();
1091    JSLock lock(SilenceAssertionsOnly);
1092
1093    JSValue value = demarshalValue(exec, valueData, valueLength);
1094    object->put(exec, propertyName, value);
1095
1096    exec->clearException();
1097    return true;
1098}
1099
1100bool NetscapePluginInstanceProxy::removeProperty(uint32_t objectID, const Identifier& propertyName)
1101{
1102    if (m_inDestroy)
1103        return false;
1104
1105    JSObject* object = m_localObjects.get(objectID);
1106    if (!object) {
1107        LOG_ERROR("NetscapePluginInstanceProxy::removeProperty: local object %u doesn't exist.", objectID);
1108        return false;
1109    }
1110
1111    Frame* frame = core([m_pluginView webFrame]);
1112    if (!frame)
1113        return false;
1114
1115    ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec();
1116    if (!object->hasProperty(exec, propertyName)) {
1117        exec->clearException();
1118        return false;
1119    }
1120
1121    JSLock lock(SilenceAssertionsOnly);
1122    object->deleteProperty(exec, propertyName);
1123    exec->clearException();
1124    return true;
1125}
1126
1127bool NetscapePluginInstanceProxy::removeProperty(uint32_t objectID, unsigned propertyName)
1128{
1129    if (m_inDestroy)
1130        return false;
1131
1132    JSObject* object = m_localObjects.get(objectID);
1133    if (!object) {
1134        LOG_ERROR("NetscapePluginInstanceProxy::removeProperty: local object %u doesn't exist.", objectID);
1135        return false;
1136    }
1137
1138    Frame* frame = core([m_pluginView webFrame]);
1139    if (!frame)
1140        return false;
1141
1142    ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec();
1143    if (!object->hasProperty(exec, propertyName)) {
1144        exec->clearException();
1145        return false;
1146    }
1147
1148    JSLock lock(SilenceAssertionsOnly);
1149    object->deleteProperty(exec, propertyName);
1150    exec->clearException();
1151    return true;
1152}
1153
1154bool NetscapePluginInstanceProxy::hasProperty(uint32_t objectID, const Identifier& propertyName)
1155{
1156    if (m_inDestroy)
1157        return false;
1158
1159    JSObject* object = m_localObjects.get(objectID);
1160    if (!object) {
1161        LOG_ERROR("NetscapePluginInstanceProxy::hasProperty: local object %u doesn't exist.", objectID);
1162        return false;
1163    }
1164
1165    Frame* frame = core([m_pluginView webFrame]);
1166    if (!frame)
1167        return false;
1168
1169    ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec();
1170    bool result = object->hasProperty(exec, propertyName);
1171    exec->clearException();
1172
1173    return result;
1174}
1175
1176bool NetscapePluginInstanceProxy::hasProperty(uint32_t objectID, unsigned propertyName)
1177{
1178    if (m_inDestroy)
1179        return false;
1180
1181    JSObject* object = m_localObjects.get(objectID);
1182    if (!object) {
1183        LOG_ERROR("NetscapePluginInstanceProxy::hasProperty: local object %u doesn't exist.", objectID);
1184        return false;
1185    }
1186
1187    Frame* frame = core([m_pluginView webFrame]);
1188    if (!frame)
1189        return false;
1190
1191    ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec();
1192    bool result = object->hasProperty(exec, propertyName);
1193    exec->clearException();
1194
1195    return result;
1196}
1197
1198bool NetscapePluginInstanceProxy::hasMethod(uint32_t objectID, const Identifier& methodName)
1199{
1200    if (m_inDestroy)
1201        return false;
1202
1203    JSObject* object = m_localObjects.get(objectID);
1204    if (!object) {
1205        LOG_ERROR("NetscapePluginInstanceProxy::hasMethod: local object %u doesn't exist.", objectID);
1206        return false;
1207    }
1208
1209    Frame* frame = core([m_pluginView webFrame]);
1210    if (!frame)
1211        return false;
1212
1213    ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec();
1214    JSLock lock(SilenceAssertionsOnly);
1215    JSValue func = object->get(exec, methodName);
1216    exec->clearException();
1217    return !func.isUndefined();
1218}
1219
1220bool NetscapePluginInstanceProxy::enumerate(uint32_t objectID, data_t& resultData, mach_msg_type_number_t& resultLength)
1221{
1222    if (m_inDestroy)
1223        return false;
1224
1225    JSObject* object = m_localObjects.get(objectID);
1226    if (!object) {
1227        LOG_ERROR("NetscapePluginInstanceProxy::enumerate: local object %u doesn't exist.", objectID);
1228        return false;
1229    }
1230
1231    Frame* frame = core([m_pluginView webFrame]);
1232    if (!frame)
1233        return false;
1234
1235    ExecState* exec = frame->script()->globalObject(pluginWorld())->globalExec();
1236    JSLock lock(SilenceAssertionsOnly);
1237
1238    PropertyNameArray propertyNames(exec);
1239    object->getPropertyNames(exec, propertyNames);
1240
1241    RetainPtr<NSMutableArray*> array(AdoptNS, [[NSMutableArray alloc] init]);
1242    for (unsigned i = 0; i < propertyNames.size(); i++) {
1243        uint64_t methodName = reinterpret_cast<uint64_t>(_NPN_GetStringIdentifier(propertyNames[i].ustring().utf8().data()));
1244
1245        [array.get() addObject:[NSNumber numberWithLongLong:methodName]];
1246    }
1247
1248    NSData *data = [NSPropertyListSerialization dataFromPropertyList:array.get() format:NSPropertyListBinaryFormat_v1_0 errorDescription:0];
1249    ASSERT(data);
1250
1251    resultLength = [data length];
1252    mig_allocate(reinterpret_cast<vm_address_t*>(&resultData), resultLength);
1253
1254    memcpy(resultData, [data bytes], resultLength);
1255
1256    exec->clearException();
1257
1258    return true;
1259}
1260
1261void NetscapePluginInstanceProxy::addValueToArray(NSMutableArray *array, ExecState* exec, JSValue value)
1262{
1263    JSLock lock(SilenceAssertionsOnly);
1264
1265    if (value.isString()) {
1266        [array addObject:[NSNumber numberWithInt:StringValueType]];
1267        [array addObject:ustringToString(value.toString(exec))];
1268    } else if (value.isNumber()) {
1269        [array addObject:[NSNumber numberWithInt:DoubleValueType]];
1270        [array addObject:[NSNumber numberWithDouble:value.toNumber(exec)]];
1271    } else if (value.isBoolean()) {
1272        [array addObject:[NSNumber numberWithInt:BoolValueType]];
1273        [array addObject:[NSNumber numberWithBool:value.toBoolean(exec)]];
1274    } else if (value.isNull())
1275        [array addObject:[NSNumber numberWithInt:NullValueType]];
1276    else if (value.isObject()) {
1277        JSObject* object = asObject(value);
1278        if (object->classInfo() == &ProxyRuntimeObject::s_info) {
1279            ProxyRuntimeObject* runtimeObject = static_cast<ProxyRuntimeObject*>(object);
1280            if (ProxyInstance* instance = runtimeObject->getInternalProxyInstance()) {
1281                [array addObject:[NSNumber numberWithInt:NPObjectValueType]];
1282                [array addObject:[NSNumber numberWithInt:instance->objectID()]];
1283            }
1284        } else {
1285            [array addObject:[NSNumber numberWithInt:JSObjectValueType]];
1286            [array addObject:[NSNumber numberWithInt:m_localObjects.idForObject(exec->globalData(), object)]];
1287        }
1288    } else
1289        [array addObject:[NSNumber numberWithInt:VoidValueType]];
1290}
1291
1292void NetscapePluginInstanceProxy::marshalValue(ExecState* exec, JSValue value, data_t& resultData, mach_msg_type_number_t& resultLength)
1293{
1294    RetainPtr<NSMutableArray*> array(AdoptNS, [[NSMutableArray alloc] init]);
1295
1296    addValueToArray(array.get(), exec, value);
1297
1298    RetainPtr<NSData *> data = [NSPropertyListSerialization dataFromPropertyList:array.get() format:NSPropertyListBinaryFormat_v1_0 errorDescription:0];
1299    ASSERT(data);
1300
1301    resultLength = [data.get() length];
1302    mig_allocate(reinterpret_cast<vm_address_t*>(&resultData), resultLength);
1303
1304    memcpy(resultData, [data.get() bytes], resultLength);
1305}
1306
1307RetainPtr<NSData *> NetscapePluginInstanceProxy::marshalValues(ExecState* exec, const ArgList& args)
1308{
1309    RetainPtr<NSMutableArray*> array(AdoptNS, [[NSMutableArray alloc] init]);
1310
1311    for (unsigned i = 0; i < args.size(); i++)
1312        addValueToArray(array.get(), exec, args.at(i));
1313
1314    RetainPtr<NSData *> data = [NSPropertyListSerialization dataFromPropertyList:array.get() format:NSPropertyListBinaryFormat_v1_0 errorDescription:0];
1315    ASSERT(data);
1316
1317    return data;
1318}
1319
1320bool NetscapePluginInstanceProxy::demarshalValueFromArray(ExecState* exec, NSArray *array, NSUInteger& index, JSValue& result)
1321{
1322    if (index == [array count])
1323        return false;
1324
1325    int type = [[array objectAtIndex:index++] intValue];
1326    switch (type) {
1327        case VoidValueType:
1328            result = jsUndefined();
1329            return true;
1330        case NullValueType:
1331            result = jsNull();
1332            return true;
1333        case BoolValueType:
1334            result = jsBoolean([[array objectAtIndex:index++] boolValue]);
1335            return true;
1336        case DoubleValueType:
1337            result = jsNumber([[array objectAtIndex:index++] doubleValue]);
1338            return true;
1339        case StringValueType: {
1340            NSString *string = [array objectAtIndex:index++];
1341
1342            result = jsString(exec, String(string));
1343            return true;
1344        }
1345        case JSObjectValueType: {
1346            uint32_t objectID = [[array objectAtIndex:index++] intValue];
1347
1348            result = m_localObjects.get(objectID);
1349            ASSERT(result);
1350            return true;
1351        }
1352        case NPObjectValueType: {
1353            uint32_t objectID = [[array objectAtIndex:index++] intValue];
1354
1355            Frame* frame = core([m_pluginView webFrame]);
1356            if (!frame)
1357                return false;
1358
1359            if (!frame->script()->canExecuteScripts(NotAboutToExecuteScript))
1360                return false;
1361
1362            RefPtr<RootObject> rootObject = frame->script()->createRootObject(m_pluginView);
1363            if (!rootObject)
1364                return false;
1365
1366            result = ProxyInstance::create(rootObject.release(), this, objectID)->createRuntimeObject(exec);
1367            return true;
1368        }
1369        default:
1370            ASSERT_NOT_REACHED();
1371            return false;
1372    }
1373}
1374
1375JSValue NetscapePluginInstanceProxy::demarshalValue(ExecState* exec, const char* valueData, mach_msg_type_number_t valueLength)
1376{
1377    RetainPtr<NSData*> data(AdoptNS, [[NSData alloc] initWithBytesNoCopy:(void*)valueData length:valueLength freeWhenDone:NO]);
1378
1379    RetainPtr<NSArray*> array = [NSPropertyListSerialization propertyListFromData:data.get()
1380                                                                 mutabilityOption:NSPropertyListImmutable
1381                                                                           format:0
1382                                                                 errorDescription:0];
1383    NSUInteger position = 0;
1384    JSValue value;
1385    bool result = demarshalValueFromArray(exec, array.get(), position, value);
1386    ASSERT_UNUSED(result, result);
1387
1388    return value;
1389}
1390
1391void NetscapePluginInstanceProxy::demarshalValues(ExecState* exec, data_t valuesData, mach_msg_type_number_t valuesLength, MarkedArgumentBuffer& result)
1392{
1393    RetainPtr<NSData*> data(AdoptNS, [[NSData alloc] initWithBytesNoCopy:valuesData length:valuesLength freeWhenDone:NO]);
1394
1395    RetainPtr<NSArray*> array = [NSPropertyListSerialization propertyListFromData:data.get()
1396                                                                 mutabilityOption:NSPropertyListImmutable
1397                                                                           format:0
1398                                                                 errorDescription:0];
1399    NSUInteger position = 0;
1400    JSValue value;
1401    while (demarshalValueFromArray(exec, array.get(), position, value))
1402        result.append(value);
1403}
1404
1405void NetscapePluginInstanceProxy::retainLocalObject(JSC::JSValue value)
1406{
1407    if (!value.isObject() || value.inherits(&ProxyRuntimeObject::s_info))
1408        return;
1409
1410    m_localObjects.retain(asObject(value));
1411}
1412
1413void NetscapePluginInstanceProxy::releaseLocalObject(JSC::JSValue value)
1414{
1415    if (!value.isObject() || value.inherits(&ProxyRuntimeObject::s_info))
1416        return;
1417
1418    m_localObjects.release(asObject(value));
1419}
1420
1421PassRefPtr<Instance> NetscapePluginInstanceProxy::createBindingsInstance(PassRefPtr<RootObject> rootObject)
1422{
1423    uint32_t requestID = nextRequestID();
1424
1425    if (_WKPHGetScriptableNPObject(m_pluginHostProxy->port(), m_pluginID, requestID) != KERN_SUCCESS)
1426        return 0;
1427
1428    auto_ptr<GetScriptableNPObjectReply> reply = waitForReply<GetScriptableNPObjectReply>(requestID);
1429    if (!reply.get())
1430        return 0;
1431
1432    if (!reply->m_objectID)
1433        return 0;
1434
1435    // Since the reply was non-null, "this" is still a valid pointer.
1436    return ProxyInstance::create(rootObject, this, reply->m_objectID);
1437}
1438
1439void NetscapePluginInstanceProxy::addInstance(ProxyInstance* instance)
1440{
1441    ASSERT(!m_instances.contains(instance));
1442
1443    m_instances.add(instance);
1444}
1445
1446void NetscapePluginInstanceProxy::removeInstance(ProxyInstance* instance)
1447{
1448    ASSERT(m_instances.contains(instance));
1449
1450    m_instances.remove(instance);
1451}
1452
1453void NetscapePluginInstanceProxy::willCallPluginFunction()
1454{
1455    m_pluginFunctionCallDepth++;
1456}
1457
1458void NetscapePluginInstanceProxy::didCallPluginFunction(bool& stopped)
1459{
1460    ASSERT(m_pluginFunctionCallDepth > 0);
1461    m_pluginFunctionCallDepth--;
1462
1463    // If -stop was called while we were calling into a plug-in function, and we're no longer
1464    // inside a plug-in function, stop now.
1465    if (!m_pluginFunctionCallDepth && m_shouldStopSoon) {
1466        m_shouldStopSoon = false;
1467        [m_pluginView stop];
1468        stopped = true;
1469    }
1470}
1471
1472bool NetscapePluginInstanceProxy::shouldStop()
1473{
1474    if (m_pluginFunctionCallDepth) {
1475        m_shouldStopSoon = true;
1476        return false;
1477    }
1478
1479    return true;
1480}
1481
1482uint32_t NetscapePluginInstanceProxy::nextRequestID()
1483{
1484    uint32_t requestID = ++m_currentRequestID;
1485
1486    // We don't want to return the HashMap empty/deleted "special keys"
1487    if (requestID == 0 || requestID == static_cast<uint32_t>(-1))
1488        return nextRequestID();
1489
1490    return requestID;
1491}
1492
1493void NetscapePluginInstanceProxy::invalidateRect(double x, double y, double width, double height)
1494{
1495    ASSERT(m_pluginView);
1496
1497    m_pluginIsWaitingForDraw = true;
1498    [m_pluginView invalidatePluginContentRect:NSMakeRect(x, y, width, height)];
1499}
1500
1501void NetscapePluginInstanceProxy::didDraw()
1502{
1503    if (!m_pluginIsWaitingForDraw)
1504        return;
1505
1506    m_pluginIsWaitingForDraw = false;
1507    _WKPHPluginInstanceDidDraw(m_pluginHostProxy->port(), m_pluginID);
1508}
1509
1510bool NetscapePluginInstanceProxy::getCookies(data_t urlData, mach_msg_type_number_t urlLength, data_t& cookiesData, mach_msg_type_number_t& cookiesLength)
1511{
1512    ASSERT(m_pluginView);
1513
1514    NSURL *url = [m_pluginView URLWithCString:urlData];
1515    if (!url)
1516        return false;
1517
1518    if (Frame* frame = core([m_pluginView webFrame])) {
1519        String cookieString = cookies(frame->document(), url);
1520        WTF::CString cookieStringUTF8 = cookieString.utf8();
1521        if (cookieStringUTF8.isNull())
1522            return false;
1523
1524        cookiesLength = cookieStringUTF8.length();
1525        mig_allocate(reinterpret_cast<vm_address_t*>(&cookiesData), cookiesLength);
1526        memcpy(cookiesData, cookieStringUTF8.data(), cookiesLength);
1527
1528        return true;
1529    }
1530
1531    return false;
1532}
1533
1534bool NetscapePluginInstanceProxy::setCookies(data_t urlData, mach_msg_type_number_t urlLength, data_t cookiesData, mach_msg_type_number_t cookiesLength)
1535{
1536    ASSERT(m_pluginView);
1537
1538    NSURL *url = [m_pluginView URLWithCString:urlData];
1539    if (!url)
1540        return false;
1541
1542    if (Frame* frame = core([m_pluginView webFrame])) {
1543        String cookieString = String::fromUTF8(cookiesData, cookiesLength);
1544        if (!cookieString)
1545            return false;
1546
1547        WebCore::setCookies(frame->document(), url, cookieString);
1548        return true;
1549    }
1550
1551    return false;
1552}
1553
1554bool NetscapePluginInstanceProxy::getProxy(data_t urlData, mach_msg_type_number_t urlLength, data_t& proxyData, mach_msg_type_number_t& proxyLength)
1555{
1556    ASSERT(m_pluginView);
1557
1558    NSURL *url = [m_pluginView URLWithCString:urlData];
1559    if (!url)
1560        return false;
1561
1562    Vector<ProxyServer> proxyServers = proxyServersForURL(url, 0);
1563    WTF::CString proxyStringUTF8 = toString(proxyServers).utf8();
1564
1565    proxyLength = proxyStringUTF8.length();
1566    mig_allocate(reinterpret_cast<vm_address_t*>(&proxyData), proxyLength);
1567    memcpy(proxyData, proxyStringUTF8.data(), proxyLength);
1568
1569    return true;
1570}
1571
1572bool NetscapePluginInstanceProxy::getAuthenticationInfo(data_t protocolData, data_t hostData, uint32_t port, data_t schemeData, data_t realmData,
1573                                                        data_t& usernameData, mach_msg_type_number_t& usernameLength, data_t& passwordData, mach_msg_type_number_t& passwordLength)
1574{
1575    WTF::CString username;
1576    WTF::CString password;
1577
1578    if (!WebKit::getAuthenticationInfo(protocolData, hostData, port, schemeData, realmData, username, password))
1579        return false;
1580
1581    usernameLength = username.length();
1582    mig_allocate(reinterpret_cast<vm_address_t*>(&usernameData), usernameLength);
1583    memcpy(usernameData, username.data(), usernameLength);
1584
1585    passwordLength = password.length();
1586    mig_allocate(reinterpret_cast<vm_address_t*>(&passwordData), passwordLength);
1587    memcpy(passwordData, password.data(), passwordLength);
1588
1589    return true;
1590}
1591
1592bool NetscapePluginInstanceProxy::convertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
1593                                               double& destX, double& destY, NPCoordinateSpace destSpace)
1594{
1595    ASSERT(m_pluginView);
1596
1597    return [m_pluginView convertFromX:sourceX andY:sourceY space:sourceSpace toX:&destX andY:&destY space:destSpace];
1598}
1599
1600uint32_t NetscapePluginInstanceProxy::checkIfAllowedToLoadURL(const char* url, const char* target)
1601{
1602    uint32_t checkID;
1603
1604    // Assign a check ID
1605    do {
1606        checkID = ++m_urlCheckCounter;
1607    } while (m_urlChecks.contains(checkID) || !m_urlCheckCounter);
1608
1609    NSString *frameName = target ? [NSString stringWithCString:target encoding:NSISOLatin1StringEncoding] : nil;
1610
1611    NSNumber *contextInfo = [[NSNumber alloc] initWithUnsignedInt:checkID];
1612    WebPluginContainerCheck *check = [WebPluginContainerCheck checkWithRequest:[m_pluginView requestWithURLCString:url]
1613                                                                        target:frameName
1614                                                                  resultObject:m_pluginView
1615                                                                      selector:@selector(_containerCheckResult:contextInfo:)
1616                                                                    controller:m_pluginView
1617                                                                   contextInfo:contextInfo];
1618
1619    [contextInfo release];
1620    m_urlChecks.set(checkID, check);
1621    [check start];
1622
1623    return checkID;
1624}
1625
1626void NetscapePluginInstanceProxy::cancelCheckIfAllowedToLoadURL(uint32_t checkID)
1627{
1628    URLCheckMap::iterator it = m_urlChecks.find(checkID);
1629    if (it == m_urlChecks.end())
1630        return;
1631
1632    WebPluginContainerCheck *check = it->second.get();
1633    [check cancel];
1634    m_urlChecks.remove(it);
1635}
1636
1637void NetscapePluginInstanceProxy::checkIfAllowedToLoadURLResult(uint32_t checkID, bool allowed)
1638{
1639    _WKPHCheckIfAllowedToLoadURLResult(m_pluginHostProxy->port(), m_pluginID, checkID, allowed);
1640}
1641
1642void NetscapePluginInstanceProxy::resolveURL(const char* url, const char* target, data_t& resolvedURLData, mach_msg_type_number_t& resolvedURLLength)
1643{
1644    ASSERT(m_pluginView);
1645
1646    WTF::CString resolvedURL = [m_pluginView resolvedURLStringForURL:url target:target];
1647
1648    resolvedURLLength = resolvedURL.length();
1649    mig_allocate(reinterpret_cast<vm_address_t*>(&resolvedURLData), resolvedURLLength);
1650    memcpy(resolvedURLData, resolvedURL.data(), resolvedURLLength);
1651}
1652
1653void NetscapePluginInstanceProxy::privateBrowsingModeDidChange(bool isPrivateBrowsingEnabled)
1654{
1655    _WKPHPluginInstancePrivateBrowsingModeDidChange(m_pluginHostProxy->port(), m_pluginID, isPrivateBrowsingEnabled);
1656}
1657
1658static String& globalExceptionString()
1659{
1660    DEFINE_STATIC_LOCAL(String, exceptionString, ());
1661    return exceptionString;
1662}
1663
1664void NetscapePluginInstanceProxy::setGlobalException(const String& exception)
1665{
1666    globalExceptionString() = exception;
1667}
1668
1669void NetscapePluginInstanceProxy::moveGlobalExceptionToExecState(ExecState* exec)
1670{
1671    if (globalExceptionString().isNull())
1672        return;
1673
1674    {
1675        JSLock lock(SilenceAssertionsOnly);
1676        throwError(exec, createError(exec, stringToUString(globalExceptionString())));
1677    }
1678
1679    globalExceptionString() = String();
1680}
1681
1682} // namespace WebKit
1683
1684#endif // USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)
1685