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