1/*
2 * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#if USE(PLUGIN_HOST_PROCESS)
27
28#ifndef NetscapePluginInstanceProxy_h
29#define NetscapePluginInstanceProxy_h
30
31#include <JavaScriptCore/Identifier.h>
32#include <JavaScriptCore/VM.h>
33#include <JavaScriptCore/Strong.h>
34#include <WebCore/Timer.h>
35#include <WebKitLegacy/npapi.h>
36#include <wtf/Deque.h>
37#include <wtf/Forward.h>
38#include <wtf/HashMap.h>
39#include <wtf/PassRefPtr.h>
40#include <wtf/RefCounted.h>
41#include <wtf/RetainPtr.h>
42#include "WebKitPluginHostTypes.h"
43
44namespace JSC {
45    namespace Bindings {
46        class Instance;
47        class RootObject;
48    }
49    class ArgList;
50}
51@class WebHostedNetscapePluginView;
52@class WebFrame;
53
54namespace WebKit {
55
56class HostedNetscapePluginStream;
57class NetscapePluginHostProxy;
58class PluginRequest;
59class ProxyInstance;
60
61class NetscapePluginInstanceProxy : public RefCounted<NetscapePluginInstanceProxy> {
62public:
63    static PassRefPtr<NetscapePluginInstanceProxy> create(NetscapePluginHostProxy*, WebHostedNetscapePluginView *, bool fullFramePlugin);
64    ~NetscapePluginInstanceProxy();
65
66    uint32_t pluginID() const
67    {
68        ASSERT(m_pluginID);
69
70        return m_pluginID;
71    }
72    uint32_t renderContextID() const { ASSERT(fastMallocSize(this)); return m_renderContextID; }
73    void setRenderContextID(uint32_t renderContextID) { m_renderContextID = renderContextID; }
74
75    RendererType rendererType() const { return m_rendererType; }
76    void setRendererType(RendererType rendererType) { m_rendererType = rendererType; }
77
78    WebHostedNetscapePluginView *pluginView() const { ASSERT(fastMallocSize(this)); return m_pluginView; }
79    NetscapePluginHostProxy* hostProxy() const { ASSERT(fastMallocSize(this)); return m_pluginHostProxy; }
80
81    bool cancelStreamLoad(uint32_t streamID, NPReason);
82    void disconnectStream(HostedNetscapePluginStream*);
83
84    void setManualStream(PassRefPtr<HostedNetscapePluginStream>);
85    HostedNetscapePluginStream* manualStream() const { return m_manualStream.get(); }
86
87    void pluginHostDied();
88
89    void resize(NSRect size, NSRect clipRect);
90    void destroy();
91    void focusChanged(bool hasFocus);
92    void windowFocusChanged(bool hasFocus);
93    void windowFrameChanged(NSRect frame);
94
95    void setShouldHostLayersInWindowServer(bool);
96    void layerHostingModeChanged(bool hostsLayersInWindowServer, uint32_t renderContextID);
97
98    void mouseEvent(NSView *pluginView, NSEvent *, NPCocoaEventType);
99    void keyEvent(NSView *pluginView, NSEvent *, NPCocoaEventType);
100    void insertText(NSString *);
101    bool wheelEvent(NSView *pluginView, NSEvent *);
102    void syntheticKeyDownWithCommandModifier(int keyCode, char character);
103    void flagsChanged(NSEvent *);
104    void print(CGContextRef, unsigned width, unsigned height);
105    void snapshot(CGContextRef, unsigned width, unsigned height);
106
107    void startTimers(bool throttleTimers);
108    void stopTimers();
109
110    void invalidateRect(double x, double y, double width, double height);
111
112    // NPRuntime
113    bool getWindowNPObject(uint32_t& objectID);
114    bool getPluginElementNPObject(uint32_t& objectID);
115    bool forgetBrowserObjectID(uint32_t objectID); // Will fail if the ID is being sent to plug-in right now (i.e., retain/release calls aren't balanced).
116
117    bool evaluate(uint32_t objectID, const WTF::String& script, data_t& resultData, mach_msg_type_number_t& resultLength, bool allowPopups);
118    bool invoke(uint32_t objectID, const JSC::Identifier& methodName, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength);
119    bool invokeDefault(uint32_t objectID, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength);
120    bool construct(uint32_t objectID, data_t argumentsData, mach_msg_type_number_t argumentsLength, data_t& resultData, mach_msg_type_number_t& resultLength);
121    bool enumerate(uint32_t objectID, data_t& resultData, mach_msg_type_number_t& resultLength);
122
123    bool getProperty(uint32_t objectID, const JSC::Identifier& propertyName, data_t &resultData, mach_msg_type_number_t& resultLength);
124    bool getProperty(uint32_t objectID, unsigned propertyName, data_t &resultData, mach_msg_type_number_t& resultLength);
125    bool setProperty(uint32_t objectID, const JSC::Identifier& propertyName, data_t valueData, mach_msg_type_number_t valueLength);
126    bool setProperty(uint32_t objectID, unsigned propertyName, data_t valueData, mach_msg_type_number_t valueLength);
127    bool removeProperty(uint32_t objectID, const JSC::Identifier& propertyName);
128    bool removeProperty(uint32_t objectID, unsigned propertyName);
129    bool hasProperty(uint32_t objectID, const JSC::Identifier& propertyName);
130    bool hasProperty(uint32_t objectID, unsigned propertyName);
131    bool hasMethod(uint32_t objectID, const JSC::Identifier& methodName);
132
133    void status(const char* message);
134    NPError loadURL(const char* url, const char* target, const char* postData, uint32_t postDataLength, LoadURLFlags, uint32_t& requestID);
135
136    bool getCookies(data_t urlData, mach_msg_type_number_t urlLength, data_t& cookiesData, mach_msg_type_number_t& cookiesLength);
137    bool setCookies(data_t urlData, mach_msg_type_number_t urlLength, data_t cookiesData, mach_msg_type_number_t cookiesLength);
138
139    bool getProxy(data_t urlData, mach_msg_type_number_t urlLength, data_t& proxyData, mach_msg_type_number_t& proxyLength);
140    bool getAuthenticationInfo(data_t protocolData, data_t hostData, uint32_t port, data_t schemeData, data_t realmData,
141                               data_t& usernameData, mach_msg_type_number_t& usernameLength, data_t& passwordData, mach_msg_type_number_t& passwordLength);
142    bool convertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace,
143                      double& destX, double& destY, NPCoordinateSpace destSpace);
144
145    PassRefPtr<JSC::Bindings::Instance> createBindingsInstance(PassRefPtr<JSC::Bindings::RootObject>);
146    RetainPtr<NSData *> marshalValues(JSC::ExecState*, const JSC::ArgList& args);
147    void marshalValue(JSC::ExecState*, JSC::JSValue, data_t& resultData, mach_msg_type_number_t& resultLength);
148    JSC::JSValue demarshalValue(JSC::ExecState*, const char* valueData, mach_msg_type_number_t valueLength);
149
150    // No-op if the value does not contain a local object.
151    void retainLocalObject(JSC::JSValue);
152    void releaseLocalObject(JSC::JSValue);
153
154    void addInstance(ProxyInstance*);
155    void removeInstance(ProxyInstance*);
156
157    void cleanup();
158    void invalidate();
159
160    void willCallPluginFunction();
161    void didCallPluginFunction(bool& stopped);
162    bool shouldStop();
163
164    uint32_t nextRequestID();
165
166    uint32_t checkIfAllowedToLoadURL(const char* url, const char* target);
167    void cancelCheckIfAllowedToLoadURL(uint32_t checkID);
168    void checkIfAllowedToLoadURLResult(uint32_t checkID, bool allowed);
169
170    void resolveURL(const char* url, const char* target, data_t& resolvedURLData, mach_msg_type_number_t& resolvedURLLength);
171
172    void didDraw();
173    void privateBrowsingModeDidChange(bool isPrivateBrowsingEnabled);
174
175    static void setGlobalException(const WTF::String&);
176    static void moveGlobalExceptionToExecState(JSC::ExecState*);
177
178    // Reply structs
179    struct Reply {
180        enum Type {
181            InstantiatePlugin,
182            GetScriptableNPObject,
183            BooleanAndData,
184            Boolean
185        };
186
187        Reply(Type type)
188            : m_type(type)
189        {
190        }
191
192        virtual ~Reply() { }
193
194        Type m_type;
195    };
196
197    struct InstantiatePluginReply : public Reply {
198        static const int ReplyType = InstantiatePlugin;
199
200        InstantiatePluginReply(kern_return_t resultCode, uint32_t renderContextID, RendererType rendererType)
201            : Reply(InstantiatePlugin)
202            , m_resultCode(resultCode)
203            , m_renderContextID(renderContextID)
204            , m_rendererType(rendererType)
205        {
206        }
207
208        kern_return_t m_resultCode;
209        uint32_t m_renderContextID;
210        RendererType m_rendererType;
211    };
212
213    struct GetScriptableNPObjectReply : public Reply {
214        static const Reply::Type ReplyType = GetScriptableNPObject;
215
216        GetScriptableNPObjectReply(uint32_t objectID)
217            : Reply(ReplyType)
218            , m_objectID(objectID)
219        {
220        }
221
222        uint32_t m_objectID;
223    };
224
225    struct BooleanReply : public Reply {
226        static const Reply::Type ReplyType = Boolean;
227
228        BooleanReply(boolean_t result)
229            : Reply(ReplyType)
230            , m_result(result)
231        {
232        }
233
234        boolean_t m_result;
235    };
236
237    struct BooleanAndDataReply : public Reply {
238        static const Reply::Type ReplyType = BooleanAndData;
239
240        BooleanAndDataReply(boolean_t returnValue, RetainPtr<CFDataRef> result)
241            : Reply(ReplyType)
242            , m_returnValue(returnValue)
243            , m_result(result)
244        {
245        }
246
247        boolean_t m_returnValue;
248        RetainPtr<CFDataRef> m_result;
249    };
250
251    void setCurrentReply(uint32_t requestID, std::unique_ptr<Reply> reply)
252    {
253        ASSERT(!m_replies.contains(requestID));
254        m_replies.add(requestID, WTF::move(reply));
255    }
256
257    template <typename T>
258    std::unique_ptr<T> waitForReply(uint32_t requestID)
259    {
260        Ref<NetscapePluginInstanceProxy> protect(*this); // Plug-in host may crash while we are waiting for reply, releasing all instances to the instance proxy.
261
262        willCallPluginFunction();
263        m_waitingForReply = true;
264
265        auto reply = processRequestsAndWaitForReply(requestID);
266        if (reply)
267            ASSERT(reply->m_type == T::ReplyType);
268
269        m_waitingForReply = false;
270
271        bool stopped = false;
272        didCallPluginFunction(stopped);
273        if (stopped) {
274            // The instance proxy may have been deleted from didCallPluginFunction(), so a null reply needs to be returned.
275            return nullptr;
276        }
277
278        return std::unique_ptr<T>(static_cast<T*>(reply.release()));
279    }
280
281    void webFrameDidFinishLoadWithReason(WebFrame*, NPReason);
282
283private:
284    NetscapePluginInstanceProxy(NetscapePluginHostProxy*, WebHostedNetscapePluginView*, bool fullFramePlugin);
285
286    NPError loadRequest(NSURLRequest*, const char* cTarget, bool currentEventIsUserGesture, uint32_t& streamID);
287
288    class PluginRequest;
289    void performRequest(PluginRequest*);
290    void evaluateJavaScript(PluginRequest*);
291
292    void stopAllStreams();
293    std::unique_ptr<Reply> processRequestsAndWaitForReply(uint32_t requestID);
294
295    NetscapePluginHostProxy* m_pluginHostProxy;
296    WebHostedNetscapePluginView *m_pluginView;
297
298    void requestTimerFired(WebCore::Timer<NetscapePluginInstanceProxy>*);
299    WebCore::Timer<NetscapePluginInstanceProxy> m_requestTimer;
300    Deque<RefPtr<PluginRequest>> m_pluginRequests;
301
302    HashMap<uint32_t, RefPtr<HostedNetscapePluginStream>> m_streams;
303
304    uint32_t m_currentURLRequestID;
305
306    uint32_t m_pluginID;
307    uint32_t m_renderContextID;
308    RendererType m_rendererType;
309
310    bool m_waitingForReply;
311    HashMap<uint32_t, std::unique_ptr<Reply>> m_replies;
312
313    // NPRuntime
314
315    void addValueToArray(NSMutableArray *, JSC::ExecState* exec, JSC::JSValue value);
316
317    bool demarshalValueFromArray(JSC::ExecState*, NSArray *array, NSUInteger& index, JSC::JSValue& result);
318    void demarshalValues(JSC::ExecState*, data_t valuesData, mach_msg_type_number_t valuesLength, JSC::MarkedArgumentBuffer& result);
319
320    class LocalObjectMap {
321        WTF_MAKE_NONCOPYABLE(LocalObjectMap);
322    public:
323        LocalObjectMap();
324        ~LocalObjectMap();
325        uint32_t idForObject(JSC::VM&, JSC::JSObject*);
326        void retain(JSC::JSObject*);
327        void release(JSC::JSObject*);
328        void clear();
329        bool forget(uint32_t);
330        bool contains(uint32_t) const;
331        JSC::JSObject* get(uint32_t) const;
332
333    private:
334        HashMap<uint32_t, JSC::Strong<JSC::JSObject>> m_idToJSObjectMap;
335        // The pair consists of object ID and a reference count. One reference belongs to remote plug-in,
336        // and the proxy will add transient references for arguments that are being sent out.
337        HashMap<JSC::JSObject*, std::pair<uint32_t, uint32_t>> m_jsObjectToIDMap;
338        uint32_t m_objectIDCounter;
339    };
340
341    LocalObjectMap m_localObjects;
342
343    typedef HashSet<ProxyInstance*> ProxyInstanceSet;
344    ProxyInstanceSet m_instances;
345
346    uint32_t m_urlCheckCounter;
347    typedef HashMap<uint32_t, RetainPtr<id>> URLCheckMap;
348    URLCheckMap m_urlChecks;
349
350    unsigned m_pluginFunctionCallDepth;
351    bool m_shouldStopSoon;
352    uint32_t m_currentRequestID;
353
354    // All NPRuntime functions will return false when destroying a plug-in. This is necessary because there may be unhandled messages waiting,
355    // and spinning in processRequests() will unexpectedly execute them from inside destroy(). That's not a good time to execute arbitrary JavaScript,
356    // since both loading and rendering data structures may be in inconsistent state.
357    // This suppresses calls from all plug-ins, even those in different pages, since JS might affect the frame with plug-in that's being stopped.
358    //
359    // FIXME: Plug-ins can execute arbitrary JS from destroy() in same process case, and other browsers also support that.
360    // A better fix may be to make sure that unrelated messages are postponed until after destroy() returns.
361    // Another possible fix may be to send destroy message at a time when internal structures are consistent.
362    //
363    // FIXME: We lack similar message suppression in other cases - resize() is also triggered by layout, so executing arbitrary JS is also problematic.
364    static bool m_inDestroy;
365
366    bool m_pluginIsWaitingForDraw;
367
368    RefPtr<HostedNetscapePluginStream> m_manualStream;
369
370    typedef HashMap<WebFrame*, RefPtr<PluginRequest>> FrameLoadMap;
371    FrameLoadMap m_pendingFrameLoads;
372};
373
374} // namespace WebKit
375
376#endif // NetscapePluginInstanceProxy_h
377#endif // USE(PLUGIN_HOST_PROCESS)
378