Fix the build after r41126.
[WebKit-https.git] / WebKit / mac / Plugins / Hosted / NetscapePluginInstanceProxy.mm
1 /*
2  * Copyright (C) 2008 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)
27
28 #import "NetscapePluginInstanceProxy.h"
29
30 #import "HostedNetscapePluginStream.h"
31 #import "NetscapePluginHostProxy.h"
32 #import "ProxyInstance.h"
33 #import "WebDataSourceInternal.h"
34 #import "WebFrameInternal.h"
35 #import "WebHostedNetscapePluginView.h"
36 #import "WebNSDataExtras.h"
37 #import "WebNSURLExtras.h"
38 #import "WebKitNSStringExtras.h"
39 #import "WebPluginRequest.h"
40 #import "WebViewInternal.h"
41 #import "WebUIDelegate.h"
42 #import "WebUIDelegatePrivate.h"
43
44 #import <mach/mach.h>
45 #import <WebCore/DocumentLoader.h>
46 #import <WebCore/Frame.h>
47 #import <WebCore/FrameLoader.h>
48 #import <WebCore/FrameTree.h>
49 #import <WebCore/runtime_object.h>
50 #import <WebCore/ScriptController.h>
51 #import <WebCore/ScriptValue.h>
52 #include <runtime/JSLock.h>
53 #import <utility>
54
55 extern "C" {
56 #import "WebKitPluginClientServer.h"
57 #import "WebKitPluginHost.h"
58 }
59
60 using namespace JSC;
61 using namespace JSC::Bindings;
62 using namespace std;
63 using namespace WebCore;
64
65 namespace WebKit {
66
67 class NetscapePluginInstanceProxy::PluginRequest {
68 public:
69     PluginRequest(uint32_t requestID, NSURLRequest *request, NSString *frameName, bool didStartFromUserGesture)
70         : m_requestID(requestID)
71         , m_request(request)
72         , m_frameName(frameName)
73         , m_didStartFromUserGesture(didStartFromUserGesture)
74     {
75     }
76     
77     uint32_t requestID() const { return m_requestID; }
78     NSURLRequest *request() const { return m_request.get(); }
79     NSString *frameName() const { return m_frameName.get(); }
80     bool didStartFromUserGesture() const { return m_didStartFromUserGesture; }
81     
82 private:
83     uint32_t m_requestID;
84     RetainPtr<NSURLRequest *> m_request;
85     RetainPtr<NSString *> m_frameName;
86     bool m_didStartFromUserGesture;
87 };
88
89 static uint32_t pluginIDCounter;
90
91 NetscapePluginInstanceProxy::NetscapePluginInstanceProxy(NetscapePluginHostProxy* pluginHostProxy, WebHostedNetscapePluginView *pluginView)
92     : m_pluginHostProxy(pluginHostProxy)
93     , m_pluginView(pluginView)
94     , m_requestTimer(this, &NetscapePluginInstanceProxy::requestTimerFired)
95     , m_currentRequestID(0)
96     , m_renderContextID(0)
97     , m_useSoftwareRenderer(false)
98     , m_waitingForReply(false)
99     , m_objectIDCounter(0)
100     , m_pluginFunctionCallDepth(0)
101     , m_shouldStopSoon(false)
102 {
103     ASSERT(m_pluginView);
104     
105     // Assign a plug-in ID.
106     do {
107         m_pluginID = ++pluginIDCounter;
108     } while (pluginHostProxy->pluginInstance(m_pluginID) || !m_pluginID);
109     
110     pluginHostProxy->addPluginInstance(this);
111 }
112
113 NetscapePluginInstanceProxy::~NetscapePluginInstanceProxy()
114 {
115     ASSERT(!m_pluginHostProxy);
116     
117     m_pluginID = 0;
118 }
119
120 void NetscapePluginInstanceProxy::resize(NSRect size, NSRect clipRect)
121 {
122     _WKPHResizePluginInstance(m_pluginHostProxy->port(), m_pluginID, size.origin.x, size.origin.y, size.size.width, size.size.height);
123 }
124
125 void NetscapePluginInstanceProxy::stopAllStreams()
126 {
127     Vector<RefPtr<HostedNetscapePluginStream> > streamsCopy;
128     copyValuesToVector(m_streams, streamsCopy);
129     for (size_t i = 0; i < streamsCopy.size(); i++)
130         streamsCopy[i]->stop();
131 }
132
133 void NetscapePluginInstanceProxy::cleanup()
134 {
135     stopAllStreams();
136     
137     // Clear the object map, this will cause any outstanding JS objects that the plug-in had a reference to 
138     // to go away when the next garbage collection takes place.
139     m_objects.clear();
140     
141     if (Frame* frame = core([m_pluginView webFrame]))
142         frame->script()->cleanupScriptObjectsForPlugin(m_pluginView);
143     
144     ProxyInstanceSet instances;
145     instances.swap(m_instances);
146     
147     // Invalidate all proxy instances.
148     ProxyInstanceSet::const_iterator end = instances.end();
149     for (ProxyInstanceSet::const_iterator it = instances.begin(); it != end; ++it)
150         (*it)->invalidate();
151 }
152
153 void NetscapePluginInstanceProxy::invalidate()
154 {
155     m_pluginHostProxy->removePluginInstance(this);
156     m_pluginHostProxy = 0;
157 }
158
159 void NetscapePluginInstanceProxy::destroy()
160 {
161     _WKPHDestroyPluginInstance(m_pluginHostProxy->port(), m_pluginID);
162
163     cleanup();
164     invalidate();
165 }
166
167 HostedNetscapePluginStream *NetscapePluginInstanceProxy::pluginStream(uint32_t streamID)
168 {
169     return m_streams.get(streamID).get();
170 }
171
172 void NetscapePluginInstanceProxy::disconnectStream(HostedNetscapePluginStream* stream)
173 {
174     m_streams.remove(stream->streamID());
175 }
176     
177 void NetscapePluginInstanceProxy::pluginHostDied()
178 {
179     m_pluginHostProxy = 0;
180
181     cleanup();
182     
183     [m_pluginView pluginHostDied];
184     m_pluginView = nil;
185 }
186
187 void NetscapePluginInstanceProxy::focusChanged(bool hasFocus)
188 {
189     _WKPHPluginInstanceFocusChanged(m_pluginHostProxy->port(), m_pluginID, hasFocus);
190 }
191
192 void NetscapePluginInstanceProxy::windowFocusChanged(bool hasFocus)
193 {
194     _WKPHPluginInstanceWindowFocusChanged(m_pluginHostProxy->port(), m_pluginID, hasFocus);
195 }
196
197 void NetscapePluginInstanceProxy::windowFrameChanged(NSRect frame)
198 {
199     _WKPHPluginInstanceWindowFrameChanged(m_pluginHostProxy->port(), m_pluginID, frame.origin.x, frame.origin.y, frame.size.width, frame.size.height,
200                                           // FIXME: Is it always correct to pass the rect of the first screen here?
201                                           NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]));
202 }
203     
204 void NetscapePluginInstanceProxy::startTimers(bool throttleTimers)
205 {
206     _WKPHPluginInstanceStartTimers(m_pluginHostProxy->port(), m_pluginID, throttleTimers);
207 }
208     
209 void NetscapePluginInstanceProxy::mouseEvent(NSView *pluginView, NSEvent *event, NPCocoaEventType type)
210 {
211     NSPoint screenPoint = [[event window] convertBaseToScreen:[event locationInWindow]];
212     NSPoint pluginPoint = [pluginView convertPoint:[event locationInWindow] fromView:nil];
213     
214     int clickCount;
215     if (type == NPCocoaEventMouseEntered || type == NPCocoaEventMouseExited)
216         clickCount = 0;
217     else
218         clickCount = [event clickCount];
219     
220
221     _WKPHPluginInstanceMouseEvent(m_pluginHostProxy->port(), m_pluginID,
222                                   [event timestamp],
223                                   type, [event modifierFlags],
224                                   pluginPoint.x, pluginPoint.y,
225                                   screenPoint.x, screenPoint.y,
226                                   // FIXME: Is it always correct to pass the rect of the first screen here?
227                                   NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]),
228                                   [event buttonNumber], clickCount, 
229                                   [event deltaX], [event deltaY], [event deltaZ]);
230 }
231     
232 void NetscapePluginInstanceProxy::keyEvent(NSView *pluginView, NSEvent *event, NPCocoaEventType type)
233 {
234     NSData *charactersData = [[event characters] dataUsingEncoding:NSUTF8StringEncoding];
235     NSData *charactersIgnoringModifiersData = [[event charactersIgnoringModifiers] dataUsingEncoding:NSUTF8StringEncoding];
236     
237     _WKPHPluginInstanceKeyboardEvent(m_pluginHostProxy->port(), m_pluginID,
238                                      [event timestamp], 
239                                      type, [event modifierFlags], 
240                                      const_cast<char*>(reinterpret_cast<const char*>([charactersData bytes])), [charactersData length], 
241                                      const_cast<char*>(reinterpret_cast<const char*>([charactersIgnoringModifiersData bytes])), [charactersIgnoringModifiersData length], 
242                                      [event isARepeat], [event keyCode]);
243 }
244
245 void NetscapePluginInstanceProxy::stopTimers()
246 {
247     _WKPHPluginInstanceStopTimers(m_pluginHostProxy->port(), m_pluginID);
248 }
249
250 void NetscapePluginInstanceProxy::status(const char* message)
251 {
252     RetainPtr<CFStringRef> status(AdoptCF, CFStringCreateWithCString(NULL, message, kCFStringEncodingUTF8));
253     
254     if (!status)
255         return;
256     
257     WebView *wv = [m_pluginView webView];
258     [[wv _UIDelegateForwarder] webView:wv setStatusText:(NSString *)status.get()];
259 }
260
261 NPError NetscapePluginInstanceProxy::loadURL(const char* url, const char* target, const char* postData, uint32_t postLen, LoadURLFlags flags, uint32_t& streamID)
262 {
263     if (!url)
264         return NPERR_INVALID_PARAM;
265  
266     NSMutableURLRequest *request = [m_pluginView requestWithURLCString:url];
267
268     if (flags & IsPost) {
269         NSData *httpBody = nil;
270
271         if (flags & PostDataIsFile) {
272             // If we're posting a file, buf is either a file URL or a path to the file.
273             RetainPtr<CFStringRef> bufString(AdoptCF, CFStringCreateWithCString(kCFAllocatorDefault, postData, kCFStringEncodingWindowsLatin1));
274             if (!bufString)
275                 return NPERR_INVALID_PARAM;
276             
277             NSURL *fileURL = [NSURL _web_URLWithDataAsString:(NSString *)bufString.get()];
278             NSString *path;
279             if ([fileURL isFileURL])
280                 path = [fileURL path];
281             else
282                 path = (NSString *)bufString.get();
283             httpBody = [NSData dataWithContentsOfFile:[path _webkit_fixedCarbonPOSIXPath]];
284             if (!httpBody)
285                 return NPERR_FILE_NOT_FOUND;
286         } else
287             httpBody = [NSData dataWithBytes:postData length:postLen];
288
289         if (![httpBody length])
290             return NPERR_INVALID_PARAM;
291
292         [request setHTTPMethod:@"POST"];
293         
294         if (flags & AllowHeadersInPostData) {
295             if ([httpBody _web_startsWithBlankLine])
296                 httpBody = [httpBody subdataWithRange:NSMakeRange(1, [httpBody length] - 1)];
297             else {
298                 NSInteger location = [httpBody _web_locationAfterFirstBlankLine];
299                 if (location != NSNotFound) {
300                     // If the blank line is somewhere in the middle of postData, everything before is the header.
301                     NSData *headerData = [httpBody subdataWithRange:NSMakeRange(0, location)];
302                     NSMutableDictionary *header = [headerData _webkit_parseRFC822HeaderFields];
303                     unsigned dataLength = [httpBody length] - location;
304
305                     // Sometimes plugins like to set Content-Length themselves when they post,
306                     // but CFNetwork does not like that. So we will remove the header
307                     // and instead truncate the data to the requested length.
308                     NSString *contentLength = [header objectForKey:@"Content-Length"];
309
310                     if (contentLength)
311                         dataLength = min(static_cast<unsigned>([contentLength intValue]), dataLength);
312                     [header removeObjectForKey:@"Content-Length"];
313
314                     if ([header count] > 0)
315                         [request setAllHTTPHeaderFields:header];
316
317                     // Everything after the blank line is the actual content of the POST.
318                     httpBody = [httpBody subdataWithRange:NSMakeRange(location, dataLength)];
319                 }
320             }
321         }
322
323         if (![httpBody length])
324             return NPERR_INVALID_PARAM;
325
326         // Plug-ins expect to receive uncached data when doing a POST (3347134).
327         [request setCachePolicy:NSURLRequestReloadIgnoringCacheData];
328         [request setHTTPBody:httpBody];
329     }
330     
331     return loadRequest(request, target, flags & CurrentEventIsUserGesture, streamID);
332 }
333
334 void NetscapePluginInstanceProxy::performRequest(PluginRequest* pluginRequest)
335 {
336     NSURLRequest *request = pluginRequest->request();
337     NSString *frameName = pluginRequest->frameName();
338     WebFrame *frame = nil;
339     
340     NSURL *URL = [request URL];
341     NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
342     
343     ASSERT(frameName || JSString);
344     if (frameName) {
345         // FIXME - need to get rid of this window creation which
346         // bypasses normal targeted link handling
347         frame = kit(core([m_pluginView webFrame])->loader()->findFrameForNavigation(frameName));
348         if (!frame) {
349             WebView *currentWebView = [m_pluginView webView];
350             NSDictionary *features = [[NSDictionary alloc] init];
351             WebView *newWebView = [[currentWebView _UIDelegateForwarder] webView:currentWebView
352                                                         createWebViewWithRequest:nil
353                                                                   windowFeatures:features];
354             [features release];
355
356             if (!newWebView) {
357                 _WKPHLoadURLNotify(m_pluginHostProxy->port(), m_pluginID, pluginRequest->requestID(), NPERR_GENERIC_ERROR);
358                 return;
359             }
360             
361             frame = [newWebView mainFrame];
362             core(frame)->tree()->setName(frameName);
363             [[newWebView _UIDelegateForwarder] webViewShow:newWebView];
364         }
365     }
366
367     if (JSString) {
368         ASSERT(!frame || [m_pluginView webFrame] == frame);
369         evaluateJavaScript(pluginRequest);
370     } else
371         [frame loadRequest:request];
372 }
373
374 void NetscapePluginInstanceProxy::evaluateJavaScript(PluginRequest* pluginRequest)
375 {
376     NSURL *URL = [pluginRequest->request() URL];
377     NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
378     ASSERT(JSString);
379     
380     NSString *result = [[m_pluginView webFrame] _stringByEvaluatingJavaScriptFromString:JSString forceUserGesture:pluginRequest->didStartFromUserGesture()];
381     
382     // Don't continue if stringByEvaluatingJavaScriptFromString caused the plug-in to stop.
383     if (!m_pluginHostProxy)
384         return;
385
386     if (pluginRequest->frameName() != nil)
387         return;
388         
389     if ([result length] > 0) {
390         // Don't call NPP_NewStream and other stream methods if there is no JS result to deliver. This is what Mozilla does.
391         NSData *JSData = [result dataUsingEncoding:NSUTF8StringEncoding];
392         
393         RefPtr<HostedNetscapePluginStream> stream = HostedNetscapePluginStream::create(this, pluginRequest->requestID(), pluginRequest->request());
394         
395         RetainPtr<NSURLResponse> response(AdoptNS, [[NSURLResponse alloc] initWithURL:URL 
396                                                                              MIMEType:@"text/plain" 
397                                                                 expectedContentLength:[JSData length]
398                                                                      textEncodingName:nil]);
399         stream->startStreamWithResponse(response.get());
400         stream->didReceiveData(0, static_cast<const char*>([JSData bytes]), [JSData length]);
401         stream->didFinishLoading(0);
402     }
403 }
404
405 void NetscapePluginInstanceProxy::requestTimerFired(Timer<NetscapePluginInstanceProxy>*)
406 {
407     ASSERT(!m_pluginRequests.isEmpty());
408     
409     PluginRequest* request = m_pluginRequests.first();
410     m_pluginRequests.removeFirst();
411     
412     if (!m_pluginRequests.isEmpty())
413         m_requestTimer.startOneShot(0);
414     
415     performRequest(request);
416     delete request;
417 }
418     
419 NPError NetscapePluginInstanceProxy::loadRequest(NSURLRequest *request, const char* cTarget, bool currentEventIsUserGesture, uint32_t& requestID)
420 {
421     NSURL *URL = [request URL];
422
423     if (!URL) 
424         return NPERR_INVALID_URL;
425
426     // Don't allow requests to be loaded when the document loader is stopping all loaders.
427     if ([[m_pluginView dataSource] _documentLoader]->isStopping())
428         return NPERR_GENERIC_ERROR;
429     
430     NSString *target = nil;
431     if (cTarget) {
432         // Find the frame given the target string.
433         target = [NSString stringWithCString:cTarget encoding:NSISOLatin1StringEncoding];
434     }
435     WebFrame *frame = [m_pluginView webFrame];
436
437     // don't let a plugin start any loads if it is no longer part of a document that is being 
438     // displayed unless the loads are in the same frame as the plugin.
439     if ([[m_pluginView dataSource] _documentLoader] != core([m_pluginView webFrame])->loader()->activeDocumentLoader() &&
440         (!cTarget || [frame findFrameNamed:target] != frame)) {
441         return NPERR_GENERIC_ERROR; 
442     }
443     
444     NSString *JSString = [URL _webkit_scriptIfJavaScriptURL];
445     if (JSString != nil) {
446         if (![[[m_pluginView webView] preferences] isJavaScriptEnabled]) {
447             // Return NPERR_GENERIC_ERROR if JS is disabled. This is what Mozilla does.
448             return NPERR_GENERIC_ERROR;
449         }
450     } else {
451         if (!FrameLoader::canLoad(URL, String(), core([m_pluginView webFrame])->document()))
452             return NPERR_GENERIC_ERROR;
453     }
454     
455     // FIXME: Handle wraparound
456     requestID = ++m_currentRequestID;
457         
458     if (cTarget || JSString) {
459         // Make when targetting a frame or evaluating a JS string, perform the request after a delay because we don't
460         // want to potentially kill the plug-in inside of its URL request.
461         
462         if (JSString && target && [frame findFrameNamed:target] != frame) {
463             // For security reasons, only allow JS requests to be made on the frame that contains the plug-in.
464             return NPERR_INVALID_PARAM;
465         }
466
467         PluginRequest* pluginRequest = new PluginRequest(requestID, request, target, currentEventIsUserGesture);
468         m_pluginRequests.append(pluginRequest);
469         m_requestTimer.startOneShot(0);
470     } else {
471         RefPtr<HostedNetscapePluginStream> stream = HostedNetscapePluginStream::create(this, requestID, request);
472
473         m_streams.add(requestID, stream);
474         stream->start();
475     }
476     
477     return NPERR_NO_ERROR;
478 }
479
480 void NetscapePluginInstanceProxy::processRequestsAndWaitForReply()
481 {
482     while (!m_currentReply.get()) {
483         if (!m_pluginHostProxy->processRequests()) {
484             m_currentReply.reset();
485             break;
486         }
487     }
488 }
489     
490 uint32_t NetscapePluginInstanceProxy::idForObject(JSObject* object)
491 {
492     uint32_t objectID = 0;
493     
494     // Assign an object ID.
495     do {
496         objectID = ++m_objectIDCounter;
497     } while (!m_objectIDCounter || m_objectIDCounter == static_cast<uint32_t>(-1) || m_objects.contains(objectID));
498     
499     m_objects.set(objectID, object);
500     
501     return objectID;
502 }
503
504 // NPRuntime support
505 bool NetscapePluginInstanceProxy::getWindowNPObject(uint32_t& objectID)
506 {
507     Frame* frame = core([m_pluginView webFrame]);
508     if (!frame)
509         return false;
510     
511     if (!frame->script()->isEnabled())
512         objectID = 0;
513     else
514         objectID = idForObject(frame->script()->windowShell()->window());
515         
516     return true;
517 }
518
519 bool NetscapePluginInstanceProxy::getPluginElementNPObject(uint32_t& objectID)
520 {
521     Frame* frame = core([m_pluginView webFrame]);
522     if (!frame)
523         return false;
524     
525     if (JSObject* object = frame->script()->jsObjectForPluginElement([m_pluginView element]))
526         objectID = idForObject(object);
527     else
528         objectID = 0;
529     
530     return true;
531 }
532
533 void NetscapePluginInstanceProxy::releaseObject(uint32_t objectID)
534 {
535     m_objects.remove(objectID);
536 }
537  
538 bool NetscapePluginInstanceProxy::evaluate(uint32_t objectID, const String& script, data_t& resultData, mach_msg_type_number_t& resultLength)
539 {
540     resultData = 0;
541     resultLength = 0;
542
543     if (!m_objects.contains(objectID))
544         return false;
545
546     Frame* frame = core([m_pluginView webFrame]);
547     if (!frame)
548         return false;
549     
550     ExecState* exec = frame->script()->globalObject()->globalExec();
551     JSValuePtr value = frame->loader()->executeScript(script).jsValue();
552     
553     marshalValue(exec, value, resultData, resultLength);
554     exec->clearException();
555     return true;
556 }
557
558 bool NetscapePluginInstanceProxy::invoke(uint32_t objectID, const Identifier& methodName, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength)
559 {
560     resultData = 0;
561     resultLength = 0;
562     
563     JSObject* object = m_objects.get(objectID);
564     if (!object)
565         return false;
566     
567     Frame* frame = core([m_pluginView webFrame]);
568     if (!frame)
569         return false;
570     
571     ExecState* exec = frame->script()->globalObject()->globalExec();
572     JSLock lock(false);
573     JSValuePtr function = object->get(exec, methodName);
574     CallData callData;
575     CallType callType = function.getCallData(callData);
576     if (callType == CallTypeNone)
577         return false;
578     
579     ArgList argList;
580     demarshalValues(exec, argumentsData, argumentsLength, argList);
581
582     ProtectedPtr<JSGlobalObject> globalObject = frame->script()->globalObject();
583     globalObject->globalData()->timeoutChecker.start();
584     JSValuePtr value = call(exec, function, callType, callData, object, argList);
585     globalObject->globalData()->timeoutChecker.stop();
586         
587     marshalValue(exec, value, resultData, resultLength);
588     exec->clearException();
589     return true;
590 }
591
592 bool NetscapePluginInstanceProxy::invokeDefault(uint32_t objectID, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength)
593 {
594     JSObject* object = m_objects.get(objectID);
595     if (!object)
596         return false;
597     
598     Frame* frame = core([m_pluginView webFrame]);
599     if (!frame)
600         return false;
601     
602     ExecState* exec = frame->script()->globalObject()->globalExec();
603     JSLock lock(false);    
604     CallData callData;
605     CallType callType = object->getCallData(callData);
606     if (callType == CallTypeNone)
607         return false;
608     
609     ArgList argList;
610     demarshalValues(exec, argumentsData, argumentsLength, argList);
611
612     ProtectedPtr<JSGlobalObject> globalObject = frame->script()->globalObject();
613     globalObject->globalData()->timeoutChecker.start();
614     JSValuePtr value = call(exec, object, callType, callData, object, argList);
615     globalObject->globalData()->timeoutChecker.stop();
616     
617     marshalValue(exec, value, resultData, resultLength);
618     exec->clearException();
619     return true;
620 }
621
622 bool NetscapePluginInstanceProxy::construct(uint32_t objectID, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength)
623 {
624     JSObject* object = m_objects.get(objectID);
625     if (!object)
626         return false;
627     
628     Frame* frame = core([m_pluginView webFrame]);
629     if (!frame)
630         return false;
631     
632     ExecState* exec = frame->script()->globalObject()->globalExec();
633     JSLock lock(false);
634
635     ConstructData constructData;
636     ConstructType constructType = object->getConstructData(constructData);
637     if (constructType == ConstructTypeNone)
638         return false;
639         
640     ArgList argList;
641     demarshalValues(exec, argumentsData, argumentsLength, argList);
642
643     ProtectedPtr<JSGlobalObject> globalObject = frame->script()->globalObject();
644     globalObject->globalData()->timeoutChecker.start();
645     JSValuePtr value = JSC::construct(exec, object, constructType, constructData, argList);
646     globalObject->globalData()->timeoutChecker.stop();
647     
648     marshalValue(exec, value, resultData, resultLength);
649     exec->clearException();
650     return true;
651 }
652
653 bool NetscapePluginInstanceProxy::getProperty(uint32_t objectID, const Identifier& propertyName, data_t& resultData, mach_msg_type_number_t& resultLength)
654 {
655     JSObject* object = m_objects.get(objectID);
656     if (!object)
657         return false;
658     
659     Frame* frame = core([m_pluginView webFrame]);
660     if (!frame)
661         return false;
662     
663     ExecState* exec = frame->script()->globalObject()->globalExec();
664     JSLock lock(false);    
665     JSValuePtr value = object->get(exec, propertyName);
666     
667     marshalValue(exec, value, resultData, resultLength);
668     exec->clearException();
669     return true;
670 }
671     
672 bool NetscapePluginInstanceProxy::getProperty(uint32_t objectID, unsigned propertyName, data_t& resultData, mach_msg_type_number_t& resultLength)
673 {
674     JSObject* object = m_objects.get(objectID);
675     if (!object)
676         return false;
677     
678     Frame* frame = core([m_pluginView webFrame]);
679     if (!frame)
680         return false;
681     
682     ExecState* exec = frame->script()->globalObject()->globalExec();
683     JSLock lock(false);    
684     JSValuePtr value = object->get(exec, propertyName);
685     
686     marshalValue(exec, value, resultData, resultLength);
687     exec->clearException();
688     return true;
689 }
690
691 bool NetscapePluginInstanceProxy::setProperty(uint32_t objectID, const Identifier& propertyName, data_t valueData, mach_msg_type_number_t valueLength)
692 {
693     JSObject* object = m_objects.get(objectID);
694     if (!object)
695         return false;
696     
697     Frame* frame = core([m_pluginView webFrame]);
698     if (!frame)
699         return false;
700     
701     ExecState* exec = frame->script()->globalObject()->globalExec();
702     JSLock lock(false);    
703
704     JSValuePtr value = demarshalValue(exec, valueData, valueLength);
705     PutPropertySlot slot;
706     object->put(exec, propertyName, value, slot);
707     
708     exec->clearException();
709     return true;
710 }
711
712 bool NetscapePluginInstanceProxy::setProperty(uint32_t objectID, unsigned propertyName, data_t valueData, mach_msg_type_number_t valueLength)
713 {
714     JSObject* object = m_objects.get(objectID);
715     if (!object)
716         return false;
717     
718     Frame* frame = core([m_pluginView webFrame]);
719     if (!frame)
720         return false;
721     
722     ExecState* exec = frame->script()->globalObject()->globalExec();
723     JSLock lock(false);    
724     
725     JSValuePtr value = demarshalValue(exec, valueData, valueLength);
726     object->put(exec, propertyName, value);
727     
728     exec->clearException();
729     return true;
730 }
731
732 bool NetscapePluginInstanceProxy::removeProperty(uint32_t objectID, const Identifier& propertyName)
733 {
734     JSObject* object = m_objects.get(objectID);
735     if (!object)
736         return false;
737     
738     Frame* frame = core([m_pluginView webFrame]);
739     if (!frame)
740         return false;
741
742     ExecState* exec = frame->script()->globalObject()->globalExec();
743     if (!object->hasProperty(exec, propertyName)) {
744         exec->clearException();
745         return false;
746     }
747     
748     JSLock lock(false);
749     object->deleteProperty(exec, propertyName);
750     exec->clearException();    
751     return true;
752 }
753     
754 bool NetscapePluginInstanceProxy::removeProperty(uint32_t objectID, unsigned propertyName)
755 {
756     JSObject* object = m_objects.get(objectID);
757     if (!object)
758         return false;
759     
760     Frame* frame = core([m_pluginView webFrame]);
761     if (!frame)
762         return false;
763     
764     ExecState* exec = frame->script()->globalObject()->globalExec();
765     if (!object->hasProperty(exec, propertyName)) {
766         exec->clearException();
767         return false;
768     }
769     
770     JSLock lock(false);
771     object->deleteProperty(exec, propertyName);
772     exec->clearException();    
773     return true;
774 }
775
776 bool NetscapePluginInstanceProxy::hasProperty(uint32_t objectID, const Identifier& propertyName)
777 {
778     JSObject* object = m_objects.get(objectID);
779     if (!object)
780         return false;
781     
782     Frame* frame = core([m_pluginView webFrame]);
783     if (!frame)
784         return false;
785     
786     ExecState* exec = frame->script()->globalObject()->globalExec();
787     bool result = object->hasProperty(exec, propertyName);
788     exec->clearException();
789     
790     return result;
791 }
792
793 bool NetscapePluginInstanceProxy::hasProperty(uint32_t objectID, unsigned propertyName)
794 {
795     JSObject* object = m_objects.get(objectID);
796     if (!object)
797         return false;
798     
799     Frame* frame = core([m_pluginView webFrame]);
800     if (!frame)
801         return false;
802     
803     ExecState* exec = frame->script()->globalObject()->globalExec();
804     bool result = object->hasProperty(exec, propertyName);
805     exec->clearException();
806     
807     return result;
808 }
809     
810 bool NetscapePluginInstanceProxy::hasMethod(uint32_t objectID, const Identifier& methodName)
811 {
812     JSObject* object = m_objects.get(objectID);
813     if (!object)
814         return false;
815
816     Frame* frame = core([m_pluginView webFrame]);
817     if (!frame)
818         return false;
819     
820     ExecState* exec = frame->script()->globalObject()->globalExec();
821     JSLock lock(false);
822     JSValuePtr func = object->get(exec, methodName);
823     exec->clearException();
824     return !func.isUndefined();
825 }
826
827 void NetscapePluginInstanceProxy::addValueToArray(NSMutableArray *array, ExecState* exec, JSValuePtr value)
828 {
829     JSLock lock(false);
830
831     if (value.isString()) {
832         [array addObject:[NSNumber numberWithInt:StringValueType]];
833         [array addObject:String(value.toString(exec))];
834     } else if (value.isNumber()) {
835         [array addObject:[NSNumber numberWithInt:DoubleValueType]];
836         [array addObject:[NSNumber numberWithDouble:value.toNumber(exec)]];
837     } else if (value.isBoolean()) {
838         [array addObject:[NSNumber numberWithInt:BoolValueType]];
839         [array addObject:[NSNumber numberWithBool:value.toBoolean(exec)]];
840     } else if (value.isNull())
841         [array addObject:[NSNumber numberWithInt:NullValueType]];
842     else if (value.isObject()) {
843         JSObject* object = asObject(value);
844         if (object->classInfo() == &RuntimeObjectImp::s_info) {
845             // FIXME: Handle ProxyInstance objects.
846             ASSERT_NOT_REACHED();
847         } else {
848             [array addObject:[NSNumber numberWithInt:JSObjectValueType]];
849             [array addObject:[NSNumber numberWithInt:idForObject(object)]];
850         }
851     } else
852         [array addObject:[NSNumber numberWithInt:VoidValueType]];
853 }
854
855 void NetscapePluginInstanceProxy::marshalValue(ExecState* exec, JSValuePtr value, data_t& resultData, mach_msg_type_number_t& resultLength)
856 {
857     RetainPtr<NSMutableArray*> array(AdoptNS, [[NSMutableArray alloc] init]);
858     
859     addValueToArray(array.get(), exec, value);
860
861     RetainPtr<NSData *> data = [NSPropertyListSerialization dataFromPropertyList:array.get() format:NSPropertyListBinaryFormat_v1_0 errorDescription:0];
862     ASSERT(data);
863     
864     resultLength = [data.get() length];
865     mig_allocate(reinterpret_cast<vm_address_t*>(&resultData), resultLength);
866     
867     memcpy(resultData, [data.get() bytes], resultLength);
868 }
869
870 RetainPtr<NSData *> NetscapePluginInstanceProxy::marshalValues(ExecState* exec, const ArgList& args)
871 {
872     RetainPtr<NSMutableArray*> array(AdoptNS, [[NSMutableArray alloc] init]);
873
874     for (unsigned i = 0; i < args.size(); i++)
875         addValueToArray(array.get(), exec, args.at(exec, i));
876
877     RetainPtr<NSData *> data = [NSPropertyListSerialization dataFromPropertyList:array.get() format:NSPropertyListBinaryFormat_v1_0 errorDescription:0];
878     ASSERT(data);
879
880     return data;
881 }    
882
883 bool NetscapePluginInstanceProxy::demarshalValueFromArray(ExecState* exec, NSArray *array, NSUInteger& index, JSValuePtr& result)
884 {
885     if (index == [array count])
886         return false;
887                   
888     int type = [[array objectAtIndex:index++] intValue];
889     switch (type) {
890         case VoidValueType:
891             result = jsUndefined();
892             return true;
893         case NullValueType:
894             result = jsNull();
895             return true;
896         case BoolValueType:
897             result = jsBoolean([[array objectAtIndex:index++] boolValue]);
898             return true;
899         case DoubleValueType:
900             result = jsNumber(exec, [[array objectAtIndex:index++] doubleValue]);
901             return true;
902         case StringValueType: {
903             NSString *string = [array objectAtIndex:index++];
904             
905             result = jsString(exec, String(string));
906             return true;
907         }
908         case JSObjectValueType: {
909             uint32_t objectID = [[array objectAtIndex:index++] intValue];
910             
911             result = m_objects.get(objectID);
912             ASSERT(result);
913             return true;
914         }
915         case NPObjectValueType: {
916             uint32_t objectID = [[array objectAtIndex:index++] intValue];
917
918             Frame* frame = core([m_pluginView webFrame]);
919             if (!frame)
920                 return false;
921             
922             if (!frame->script()->isEnabled())
923                 return false;
924
925             RefPtr<RootObject> rootObject = frame->script()->createRootObject(m_pluginView);
926             if (!rootObject)
927                 return false;
928             
929             result = ProxyInstance::create(rootObject.release(), this, objectID)->createRuntimeObject(exec);
930             return true;
931         }
932         default:
933             ASSERT_NOT_REACHED();
934             return false;
935     }
936 }
937
938 JSValuePtr NetscapePluginInstanceProxy::demarshalValue(ExecState* exec, const char* valueData, mach_msg_type_number_t valueLength)
939 {
940     RetainPtr<NSData*> data(AdoptNS, [[NSData alloc] initWithBytesNoCopy:(void*)valueData length:valueLength freeWhenDone:NO]);
941
942     RetainPtr<NSArray*> array = [NSPropertyListSerialization propertyListFromData:data.get()
943                                                                  mutabilityOption:NSPropertyListImmutable
944                                                                            format:0
945                                                                  errorDescription:0];
946     NSUInteger position = 0;
947     JSValuePtr value;
948     bool result = demarshalValueFromArray(exec, array.get(), position, value);
949     ASSERT_UNUSED(result, result);
950
951     return value;
952 }
953
954 void NetscapePluginInstanceProxy::demarshalValues(ExecState* exec, data_t valuesData, mach_msg_type_number_t valuesLength, ArgList& result)
955 {
956     RetainPtr<NSData*> data(AdoptNS, [[NSData alloc] initWithBytesNoCopy:valuesData length:valuesLength freeWhenDone:NO]);
957
958     RetainPtr<NSArray*> array = [NSPropertyListSerialization propertyListFromData:data.get()
959                                                                  mutabilityOption:NSPropertyListImmutable
960                                                                            format:0
961                                                                  errorDescription:0];
962     NSUInteger position = 0;
963     JSValuePtr value;
964     while (demarshalValueFromArray(exec, array.get(), position, value))
965         result.append(value);
966 }
967
968 PassRefPtr<Instance> NetscapePluginInstanceProxy::createBindingsInstance(PassRefPtr<RootObject> rootObject)
969 {
970     if (_WKPHGetScriptableNPObject(m_pluginHostProxy->port(), m_pluginID) != KERN_SUCCESS)
971         return 0;
972     
973     auto_ptr<GetScriptableNPObjectReply> reply = waitForReply<GetScriptableNPObjectReply>();
974     if (!reply.get())
975         return 0;
976
977     if (!reply->m_objectID)
978         return 0;
979
980     return ProxyInstance::create(rootObject, this, reply->m_objectID);
981 }
982
983 void NetscapePluginInstanceProxy::addInstance(ProxyInstance* instance)
984 {
985     ASSERT(!m_instances.contains(instance));
986     
987     m_instances.add(instance);
988 }
989     
990 void NetscapePluginInstanceProxy::removeInstance(ProxyInstance* instance)
991 {
992     ASSERT(m_instances.contains(instance));
993     
994     m_instances.remove(instance);
995 }
996  
997 void NetscapePluginInstanceProxy::willCallPluginFunction()
998 {
999     m_pluginFunctionCallDepth++;
1000 }
1001     
1002 void NetscapePluginInstanceProxy::didCallPluginFunction()
1003 {
1004     ASSERT(m_pluginFunctionCallDepth > 0);
1005     m_pluginFunctionCallDepth--;
1006     
1007     // If -stop was called while we were calling into a plug-in function, and we're no longer
1008     // inside a plug-in function, stop now.
1009     if (!m_pluginFunctionCallDepth && m_shouldStopSoon) {
1010         m_shouldStopSoon = false;
1011         [m_pluginView stop];
1012     }
1013 }
1014     
1015 bool NetscapePluginInstanceProxy::shouldStop()
1016 {
1017     if (m_pluginFunctionCallDepth) {
1018         m_shouldStopSoon = true;
1019         return false;
1020     }
1021     
1022     return true;
1023 }
1024
1025 } // namespace WebKit
1026
1027 #endif // USE(PLUGIN_HOST_PROCESS)