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