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