1/*
2 * Copyright (C) 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. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "NetscapeBrowserFuncs.h"
28
29#if ENABLE(NETSCAPE_PLUGIN_API)
30
31#include "NPRuntimeUtilities.h"
32#include "NetscapePlugin.h"
33#include "PluginController.h"
34#include <WebCore/HTTPHeaderMap.h>
35#include <WebCore/HTTPHeaderNames.h>
36#include <WebCore/IdentifierRep.h>
37#include <WebCore/NotImplemented.h>
38#include <WebCore/ProtectionSpace.h>
39#include <WebCore/SharedBuffer.h>
40#include <memory>
41#include <utility>
42#include <wtf/text/StringBuilder.h>
43
44using namespace WebCore;
45
46namespace WebKit {
47
48// Helper class for delaying destruction of a plug-in.
49class PluginDestructionProtector {
50public:
51    explicit PluginDestructionProtector(NetscapePlugin* plugin)
52    {
53        if (plugin)
54            m_protector = std::make_unique<PluginController::PluginDestructionProtector>(static_cast<Plugin*>(plugin)->controller());
55    }
56
57private:
58    std::unique_ptr<PluginController::PluginDestructionProtector> m_protector;
59};
60
61static bool startsWithBlankLine(const char* bytes, unsigned length)
62{
63    return length > 0 && bytes[0] == '\n';
64}
65
66static int locationAfterFirstBlankLine(const char* bytes, unsigned length)
67{
68    for (unsigned i = 0; i < length - 4; i++) {
69        // Support for Acrobat. It sends "\n\n".
70        if (bytes[i] == '\n' && bytes[i + 1] == '\n')
71            return i + 2;
72
73        // Returns the position after 2 CRLF's or 1 CRLF if it is the first line.
74        if (bytes[i] == '\r' && bytes[i + 1] == '\n') {
75            i += 2;
76            if (i == 2)
77                return i;
78
79            if (bytes[i] == '\n') {
80                // Support for Director. It sends "\r\n\n" (3880387).
81                return i + 1;
82            }
83
84            if (bytes[i] == '\r' && bytes[i + 1] == '\n') {
85                // Support for Flash. It sends "\r\n\r\n" (3758113).
86                return i + 2;
87            }
88        }
89    }
90
91    return -1;
92}
93
94static const char* findEndOfLine(const char* bytes, unsigned length)
95{
96    // According to the HTTP specification EOL is defined as
97    // a CRLF pair. Unfortunately, some servers will use LF
98    // instead. Worse yet, some servers will use a combination
99    // of both (e.g. <header>CRLFLF<body>), so findEOL needs
100    // to be more forgiving. It will now accept CRLF, LF or
101    // CR.
102    //
103    // It returns 0 if EOLF is not found or it will return
104    // a pointer to the first terminating character.
105    for (unsigned i = 0; i < length; i++) {
106        if (bytes[i] == '\n')
107            return bytes + i;
108        if (bytes[i] == '\r') {
109            // Check to see if spanning buffer bounds
110            // (CRLF is across reads). If so, wait for
111            // next read.
112            if (i + 1 == length)
113                break;
114
115            return bytes + i;
116        }
117    }
118
119    return 0;
120}
121
122static String capitalizeRFC822HeaderFieldName(const String& name)
123{
124    bool capitalizeCharacter = true;
125    StringBuilder result;
126
127    for (unsigned i = 0; i < name.length(); i++) {
128        UChar c;
129
130        if (capitalizeCharacter && name[i] >= 'a' && name[i] <= 'z')
131            c = toASCIIUpper(name[i]);
132        else if (!capitalizeCharacter && name[i] >= 'A' && name[i] <= 'Z')
133            c = toASCIILower(name[i]);
134        else
135            c = name[i];
136
137        if (name[i] == '-')
138            capitalizeCharacter = true;
139        else
140            capitalizeCharacter = false;
141
142        result.append(c);
143    }
144
145    return result.toString();
146}
147
148static HTTPHeaderMap parseRFC822HeaderFields(const char* bytes, unsigned length)
149{
150    String lastHeaderKey;
151    HTTPHeaderMap headerFields;
152
153    // Loop over lines until we're past the header, or we can't find any more end-of-lines
154    while (const char* endOfLine = findEndOfLine(bytes, length)) {
155        const char* line = bytes;
156        int lineLength = endOfLine - bytes;
157
158        // Move bytes to the character after the terminator as returned by findEndOfLine.
159        bytes = endOfLine + 1;
160        if ((*endOfLine == '\r') && (*bytes == '\n'))
161            bytes++; // Safe since findEndOfLine won't return a spanning CRLF.
162
163        length -= (bytes - line);
164        if (!lineLength) {
165            // Blank line; we're at the end of the header
166            break;
167        }
168
169        if (*line == ' ' || *line == '\t') {
170            // Continuation of the previous header
171            if (lastHeaderKey.isNull()) {
172                // malformed header; ignore it and continue
173                continue;
174            }
175
176            // Merge the continuation of the previous header
177            String currentValue = headerFields.get(lastHeaderKey);
178            String newValue(line, lineLength);
179
180            headerFields.set(lastHeaderKey, currentValue + newValue);
181        } else {
182            // Brand new header
183            const char* colon = line;
184            while (*colon != ':' && colon != endOfLine)
185                colon++;
186
187            if (colon == endOfLine) {
188                // malformed header; ignore it and continue
189                continue;
190            }
191
192            lastHeaderKey = capitalizeRFC822HeaderFieldName(String(line, colon - line));
193            String value;
194
195            for (colon++; colon != endOfLine; colon++) {
196                if (*colon != ' ' && *colon != '\t')
197                    break;
198            }
199            if (colon == endOfLine)
200                value = "";
201            else
202                value = String(colon, endOfLine - colon);
203
204            String oldValue = headerFields.get(lastHeaderKey);
205            if (!oldValue.isNull())
206                value = oldValue + ", " + value;
207
208            headerFields.set(lastHeaderKey, value);
209        }
210    }
211
212    return headerFields;
213}
214
215static NPError parsePostBuffer(bool isFile, const char *buffer, uint32_t length, bool parseHeaders, HTTPHeaderMap& headerFields, Vector<uint8_t>& bodyData)
216{
217    RefPtr<SharedBuffer> fileContents;
218    const char* postBuffer = 0;
219    uint32_t postBufferSize = 0;
220
221    if (isFile) {
222        fileContents = SharedBuffer::createWithContentsOfFile(String::fromUTF8(buffer));
223        if (!fileContents)
224            return NPERR_FILE_NOT_FOUND;
225
226        postBuffer = fileContents->data();
227        postBufferSize = fileContents->size();
228
229        // FIXME: The NPAPI spec states that the file should be deleted here.
230    } else {
231        postBuffer = buffer;
232        postBufferSize = length;
233    }
234
235    if (parseHeaders) {
236        if (startsWithBlankLine(postBuffer, postBufferSize)) {
237            postBuffer++;
238            postBufferSize--;
239        } else {
240            int location = locationAfterFirstBlankLine(postBuffer, postBufferSize);
241            if (location != -1) {
242                // If the blank line is somewhere in the middle of the buffer, everything before is the header
243                headerFields = parseRFC822HeaderFields(postBuffer, location);
244                unsigned dataLength = postBufferSize - location;
245
246                // Sometimes plugins like to set Content-Length themselves when they post,
247                // but WebFoundation does not like that. So we will remove the header
248                // and instead truncate the data to the requested length.
249                String contentLength = headerFields.get(HTTPHeaderName::ContentLength);
250
251                if (!contentLength.isNull())
252                    dataLength = std::min(contentLength.toInt(), (int)dataLength);
253                headerFields.remove(HTTPHeaderName::ContentLength);
254
255                postBuffer += location;
256                postBufferSize = dataLength;
257            }
258        }
259    }
260
261    ASSERT(bodyData.isEmpty());
262    bodyData.append(postBuffer, postBufferSize);
263
264    return NPERR_NO_ERROR;
265}
266
267static String makeURLString(const char* url)
268{
269    String urlString(url);
270
271    // Strip return characters.
272    urlString.replaceWithLiteral('\r', "");
273    urlString.replaceWithLiteral('\n', "");
274
275    return urlString;
276}
277
278static NPError NPN_GetURL(NPP npp, const char* url, const char* target)
279{
280    if (!url)
281        return NPERR_GENERIC_ERROR;
282
283    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
284    plugin->loadURL("GET", makeURLString(url), target, HTTPHeaderMap(), Vector<uint8_t>(), false, 0);
285
286    return NPERR_GENERIC_ERROR;
287}
288
289static NPError NPN_PostURL(NPP npp, const char* url, const char* target, uint32_t len, const char* buf, NPBool file)
290{
291    HTTPHeaderMap headerFields;
292    Vector<uint8_t> postData;
293
294    // NPN_PostURL only allows headers if the post buffer points to a file.
295    bool parseHeaders = file;
296
297    NPError error = parsePostBuffer(file, buf, len, parseHeaders, headerFields, postData);
298    if (error != NPERR_NO_ERROR)
299        return error;
300
301    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
302    plugin->loadURL("POST", makeURLString(url), target, WTF::move(headerFields), postData, false, 0);
303    return NPERR_NO_ERROR;
304}
305
306static NPError NPN_RequestRead(NPStream*, NPByteRange*)
307{
308    notImplemented();
309    return NPERR_GENERIC_ERROR;
310}
311
312static NPError NPN_NewStream(NPP, NPMIMEType, const char*, NPStream**)
313{
314    notImplemented();
315    return NPERR_GENERIC_ERROR;
316}
317
318static int32_t NPN_Write(NPP, NPStream*, int32_t, void*)
319{
320    notImplemented();
321    return -1;
322}
323
324static NPError NPN_DestroyStream(NPP npp, NPStream* stream, NPReason reason)
325{
326    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
327
328    return plugin->destroyStream(stream, reason);
329}
330
331static void NPN_Status(NPP npp, const char* message)
332{
333    String statusbarText;
334    if (!message)
335        statusbarText = "";
336    else
337        statusbarText = String::fromUTF8WithLatin1Fallback(message, strlen(message));
338
339    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
340    plugin->setStatusbarText(statusbarText);
341}
342
343static const char* NPN_UserAgent(NPP npp)
344{
345    return NetscapePlugin::userAgent(npp);
346}
347
348static void* NPN_MemAlloc(uint32_t size)
349{
350    return npnMemAlloc(size);
351}
352
353static void NPN_MemFree(void* ptr)
354{
355    npnMemFree(ptr);
356}
357
358static uint32_t NPN_MemFlush(uint32_t)
359{
360    return 0;
361}
362
363static void NPN_ReloadPlugins(NPBool)
364{
365    notImplemented();
366}
367
368static JRIEnv* NPN_GetJavaEnv(void)
369{
370    notImplemented();
371    return 0;
372}
373
374static jref NPN_GetJavaPeer(NPP)
375{
376    notImplemented();
377    return 0;
378}
379
380static NPError NPN_GetURLNotify(NPP npp, const char* url, const char* target, void* notifyData)
381{
382    if (!url)
383        return NPERR_GENERIC_ERROR;
384
385    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
386    plugin->loadURL("GET", makeURLString(url), target, HTTPHeaderMap(), Vector<uint8_t>(), true, notifyData);
387
388    return NPERR_NO_ERROR;
389}
390
391static NPError NPN_PostURLNotify(NPP npp, const char* url, const char* target, uint32_t len, const char* buf, NPBool file, void* notifyData)
392{
393    HTTPHeaderMap headerFields;
394    Vector<uint8_t> postData;
395    NPError error = parsePostBuffer(file, buf, len, true, headerFields, postData);
396    if (error != NPERR_NO_ERROR)
397        return error;
398
399    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
400    plugin->loadURL("POST", makeURLString(url), target, headerFields, postData, true, notifyData);
401    return NPERR_NO_ERROR;
402}
403
404#if PLATFORM(COCOA)
405// Whether the browser supports compositing of Core Animation plug-ins.
406static const unsigned WKNVSupportsCompositingCoreAnimationPluginsBool = 74656;
407
408// Whether the browser expects a non-retained Core Animation layer.
409static const unsigned WKNVExpectsNonretainedLayer = 74657;
410
411// 74658 and 74659 are no longer implemented.
412
413static const unsigned WKNVPlugInContainer = 74660;
414
415#endif
416
417static NPError NPN_GetValue(NPP npp, NPNVariable variable, void *value)
418{
419    switch (static_cast<unsigned>(variable)) {
420        case NPNVWindowNPObject: {
421            RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
422            PluginDestructionProtector protector(plugin.get());
423
424            NPObject* windowNPObject = plugin->windowScriptNPObject();
425            if (!windowNPObject)
426                return NPERR_GENERIC_ERROR;
427
428            *(NPObject**)value = windowNPObject;
429            break;
430        }
431        case NPNVPluginElementNPObject: {
432            RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
433            PluginDestructionProtector protector(plugin.get());
434
435            NPObject* pluginElementNPObject = plugin->pluginElementNPObject();
436            *(NPObject**)value = pluginElementNPObject;
437            break;
438        }
439        case NPNVprivateModeBool: {
440            RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
441
442            *(NPBool*)value = plugin->isPrivateBrowsingEnabled();
443            break;
444        }
445#if PLATFORM(COCOA)
446        case NPNVsupportsCoreGraphicsBool:
447            // Always claim to support the Core Graphics drawing model.
448            *(NPBool*)value = true;
449            break;
450
451        case WKNVSupportsCompositingCoreAnimationPluginsBool:
452        case NPNVsupportsCoreAnimationBool: {
453            RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
454
455            *(NPBool*)value = plugin->isAcceleratedCompositingEnabled();
456            break;
457        }
458        case NPNVcontentsScaleFactor: {
459            RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
460
461            *(double*)value = plugin->contentsScaleFactor();
462            break;
463        }
464        case NPNVsupportsCocoaBool:
465            // Always claim to support the Cocoa event model.
466            *(NPBool*)value = true;
467            break;
468
469        case NPNVsupportsUpdatedCocoaTextInputBool: {
470            // The plug-in is asking whether we support the updated Cocoa text input model.
471            // If we haven't yet delivered a key down event to the plug-in, we can opt into the updated
472            // model and say that we support it. Otherwise, we'll just fall back and say that we don't support it.
473            RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
474
475            bool supportsUpdatedTextInput = !plugin->hasHandledAKeyDownEvent();
476            if (supportsUpdatedTextInput)
477                plugin->setPluginWantsLegacyCocoaTextInput(false);
478
479            *reinterpret_cast<NPBool*>(value) = supportsUpdatedTextInput;
480            break;
481        }
482
483        case WKNVCALayerRenderServerPort: {
484            RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
485
486            *(mach_port_t*)value = plugin->compositingRenderServerPort();
487            break;
488        }
489
490        case WKNVExpectsNonretainedLayer: {
491            RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
492
493            // Asking for this will make us expect a non-retained layer from the plug-in.
494            plugin->setPluginReturnsNonretainedLayer(true);
495            *(NPBool*)value = true;
496            break;
497        }
498
499        case WKNVPlugInContainer: {
500            RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
501            *reinterpret_cast<void**>(value) = plugin->plugInContainer();
502            break;
503        }
504
505#ifndef NP_NO_QUICKDRAW
506        case NPNVsupportsQuickDrawBool:
507            // We don't support the QuickDraw drawing model.
508            *(NPBool*)value = false;
509            break;
510#endif
511#ifndef NP_NO_CARBON
512       case NPNVsupportsCarbonBool:
513            *(NPBool*)value = true;
514            break;
515#endif
516#elif PLUGIN_ARCHITECTURE(X11)
517       case NPNVxDisplay: {
518           if (!npp)
519               return NPERR_GENERIC_ERROR;
520           *reinterpret_cast<Display**>(value) = NetscapePlugin::x11HostDisplay();
521           break;
522       }
523       case NPNVSupportsXEmbedBool:
524           *static_cast<NPBool*>(value) = true;
525           break;
526       case NPNVSupportsWindowless:
527           *static_cast<NPBool*>(value) = true;
528           break;
529
530       case NPNVToolkit: {
531           // Gtk based plugins need to be assured about the toolkit version.
532           const uint32_t expectedGtkToolKitVersion = 2;
533           *reinterpret_cast<uint32_t*>(value) = expectedGtkToolKitVersion;
534           break;
535       }
536
537       // TODO: implement NPNVnetscapeWindow once we want to support windowed plugins.
538#endif
539        default:
540            notImplemented();
541            return NPERR_GENERIC_ERROR;
542    }
543
544    return NPERR_NO_ERROR;
545}
546
547static NPError NPN_SetValue(NPP npp, NPPVariable variable, void *value)
548{
549    switch (variable) {
550#if PLATFORM(COCOA)
551        case NPPVpluginDrawingModel: {
552            RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
553
554            NPDrawingModel drawingModel = static_cast<NPDrawingModel>(reinterpret_cast<uintptr_t>(value));
555            return plugin->setDrawingModel(drawingModel);
556        }
557
558        case NPPVpluginEventModel: {
559            RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
560
561            NPEventModel eventModel = static_cast<NPEventModel>(reinterpret_cast<uintptr_t>(value));
562            return plugin->setEventModel(eventModel);
563        }
564#endif
565
566        case NPPVpluginWindowBool: {
567            RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
568            plugin->setIsWindowed(value);
569            return NPERR_NO_ERROR;
570        }
571
572        case NPPVpluginTransparentBool: {
573            RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
574            plugin->setIsTransparent(value);
575            return NPERR_NO_ERROR;
576        }
577
578        default:
579            notImplemented();
580            return NPERR_GENERIC_ERROR;
581    }
582}
583
584static void NPN_InvalidateRect(NPP npp, NPRect* invalidRect)
585{
586#if PLUGIN_ARCHITECTURE(X11)
587    // NSPluginWrapper, a plugin wrapper binary that allows running 32-bit plugins
588    // on 64-bit architectures typically used in X11, will sometimes give us a null NPP here.
589    if (!npp)
590        return;
591#endif
592    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
593    plugin->invalidate(invalidRect);
594}
595
596static void NPN_InvalidateRegion(NPP npp, NPRegion)
597{
598    // FIXME: We could at least figure out the bounding rectangle of the invalid region.
599    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
600    plugin->invalidate(0);
601}
602
603static void NPN_ForceRedraw(NPP)
604{
605    notImplemented();
606}
607
608static NPIdentifier NPN_GetStringIdentifier(const NPUTF8 *name)
609{
610    return static_cast<NPIdentifier>(IdentifierRep::get(name));
611}
612
613static void NPN_GetStringIdentifiers(const NPUTF8 **names, int32_t nameCount, NPIdentifier *identifiers)
614{
615    ASSERT(names);
616    ASSERT(identifiers);
617
618    if (!names || !identifiers)
619        return;
620
621    for (int32_t i = 0; i < nameCount; ++i)
622        identifiers[i] = NPN_GetStringIdentifier(names[i]);
623}
624
625static NPIdentifier NPN_GetIntIdentifier(int32_t intid)
626{
627    return static_cast<NPIdentifier>(IdentifierRep::get(intid));
628}
629
630static bool NPN_IdentifierIsString(NPIdentifier identifier)
631{
632    return static_cast<IdentifierRep*>(identifier)->isString();
633}
634
635static NPUTF8 *NPN_UTF8FromIdentifier(NPIdentifier identifier)
636{
637    const char* string = static_cast<IdentifierRep*>(identifier)->string();
638    if (!string)
639        return 0;
640
641    uint32_t stringLength = strlen(string);
642    char* utf8String = npnMemNewArray<char>(stringLength + 1);
643    memcpy(utf8String, string, stringLength);
644    utf8String[stringLength] = '\0';
645
646    return utf8String;
647}
648
649static int32_t NPN_IntFromIdentifier(NPIdentifier identifier)
650{
651    return static_cast<IdentifierRep*>(identifier)->number();
652}
653
654static NPObject* NPN_CreateObject(NPP npp, NPClass *npClass)
655{
656    return createNPObject(npp, npClass);
657}
658
659static NPObject *NPN_RetainObject(NPObject *npObject)
660{
661    retainNPObject(npObject);
662    return npObject;
663}
664
665static void NPN_ReleaseObject(NPObject *npObject)
666{
667    releaseNPObject(npObject);
668}
669
670static bool NPN_Invoke(NPP npp, NPObject *npObject, NPIdentifier methodName, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
671{
672    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
673    PluginDestructionProtector protector(plugin.get());
674
675    if (npObject->_class->invoke)
676        return npObject->_class->invoke(npObject, methodName, arguments, argumentCount, result);
677
678    return false;
679}
680
681static bool NPN_InvokeDefault(NPP npp, NPObject *npObject, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
682{
683    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
684    PluginDestructionProtector protector(plugin.get());
685
686    if (npObject->_class->invokeDefault)
687        return npObject->_class->invokeDefault(npObject, arguments, argumentCount, result);
688
689    return false;
690}
691
692static bool NPN_Evaluate(NPP npp, NPObject *npObject, NPString *script, NPVariant* result)
693{
694    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
695    PluginDestructionProtector protector(plugin.get());
696
697    String scriptString = String::fromUTF8WithLatin1Fallback(script->UTF8Characters, script->UTF8Length);
698
699    return plugin->evaluate(npObject, scriptString, result);
700}
701
702static bool NPN_GetProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName, NPVariant* result)
703{
704    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
705    PluginDestructionProtector protector(plugin.get());
706
707    if (npObject->_class->getProperty)
708        return npObject->_class->getProperty(npObject, propertyName, result);
709
710    return false;
711}
712
713static bool NPN_SetProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName, const NPVariant* value)
714{
715    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
716    PluginDestructionProtector protector(plugin.get());
717
718    if (npObject->_class->setProperty)
719        return npObject->_class->setProperty(npObject, propertyName, value);
720
721    return false;
722}
723
724static bool NPN_RemoveProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName)
725{
726    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
727    PluginDestructionProtector protector(plugin.get());
728
729    if (npObject->_class->removeProperty)
730        return npObject->_class->removeProperty(npObject, propertyName);
731
732    return false;
733}
734
735static bool NPN_HasProperty(NPP npp, NPObject* npObject, NPIdentifier propertyName)
736{
737    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
738    PluginDestructionProtector protector(plugin.get());
739
740    if (npObject->_class->hasProperty)
741        return npObject->_class->hasProperty(npObject, propertyName);
742
743    return false;
744}
745
746static bool NPN_HasMethod(NPP npp, NPObject* npObject, NPIdentifier methodName)
747{
748    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
749    PluginDestructionProtector protector(plugin.get());
750
751    if (npObject->_class->hasMethod)
752        return npObject->_class->hasMethod(npObject, methodName);
753
754    return false;
755}
756
757static void NPN_ReleaseVariantValue(NPVariant* variant)
758{
759    releaseNPVariantValue(variant);
760}
761
762static void NPN_SetException(NPObject*, const NPUTF8* message)
763{
764    NetscapePlugin::setException(message);
765}
766
767static void NPN_PushPopupsEnabledState(NPP npp, NPBool enabled)
768{
769    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
770    plugin->pushPopupsEnabledState(enabled);
771}
772
773static void NPN_PopPopupsEnabledState(NPP npp)
774{
775    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
776    plugin->popPopupsEnabledState();
777}
778
779static bool NPN_Enumerate(NPP npp, NPObject* npObject, NPIdentifier** identifiers, uint32_t* identifierCount)
780{
781    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
782    PluginDestructionProtector protector(plugin.get());
783
784    if (NP_CLASS_STRUCT_VERSION_HAS_ENUM(npObject->_class) && npObject->_class->enumerate)
785        return npObject->_class->enumerate(npObject, identifiers, identifierCount);
786
787    return false;
788}
789
790static void NPN_PluginThreadAsyncCall(NPP npp, void (*function)(void*), void* userData)
791{
792    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
793
794    plugin->pluginThreadAsyncCall(function, userData);
795}
796
797static bool NPN_Construct(NPP npp, NPObject* npObject, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
798{
799    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
800    PluginDestructionProtector protector(plugin.get());
801
802    if (NP_CLASS_STRUCT_VERSION_HAS_CTOR(npObject->_class) && npObject->_class->construct)
803        return npObject->_class->construct(npObject, arguments, argumentCount, result);
804
805    return false;
806}
807
808static NPError copyCString(const CString& string, char** value, uint32_t* len)
809{
810    ASSERT(!string.isNull());
811    ASSERT(value);
812    ASSERT(len);
813
814    *value = npnMemNewArray<char>(string.length());
815    if (!*value)
816        return NPERR_GENERIC_ERROR;
817
818    memcpy(*value, string.data(), string.length());
819    *len = string.length();
820    return NPERR_NO_ERROR;
821}
822
823static NPError NPN_GetValueForURL(NPP npp, NPNURLVariable variable, const char* url, char** value, uint32_t* len)
824{
825    if (!value || !len)
826        return NPERR_GENERIC_ERROR;
827
828    switch (variable) {
829        case NPNURLVCookie: {
830            RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
831            PluginDestructionProtector protector(plugin.get());
832
833            String cookies = plugin->cookiesForURL(makeURLString(url));
834            if (cookies.isNull())
835                return NPERR_GENERIC_ERROR;
836
837            return copyCString(cookies.utf8(), value, len);
838        }
839
840        case NPNURLVProxy: {
841            RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
842            PluginDestructionProtector protector(plugin.get());
843
844            String proxies = plugin->proxiesForURL(makeURLString(url));
845            if (proxies.isNull())
846                return NPERR_GENERIC_ERROR;
847
848            return copyCString(proxies.utf8(), value, len);
849        }
850        default:
851            notImplemented();
852            return NPERR_GENERIC_ERROR;
853    }
854}
855
856static NPError NPN_SetValueForURL(NPP npp, NPNURLVariable variable, const char* url, const char* value, uint32_t len)
857{
858    switch (variable) {
859        case NPNURLVCookie: {
860            RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
861            PluginDestructionProtector protector(plugin.get());
862
863            plugin->setCookiesForURL(makeURLString(url), String(value, len));
864            return NPERR_NO_ERROR;
865        }
866
867        case NPNURLVProxy:
868            // Can't set the proxy for a URL.
869            return NPERR_GENERIC_ERROR;
870
871        default:
872            notImplemented();
873            return NPERR_GENERIC_ERROR;
874    }
875}
876
877static bool initializeProtectionSpace(const char* protocol, const char* host, int port, const char* scheme, const char* realm, ProtectionSpace& protectionSpace)
878{
879    ProtectionSpaceServerType serverType;
880    if (!strcasecmp(protocol, "http"))
881        serverType = ProtectionSpaceServerHTTP;
882    else if (!strcasecmp(protocol, "https"))
883        serverType = ProtectionSpaceServerHTTPS;
884    else {
885        // We only care about http and https.
886        return false;
887    }
888
889    ProtectionSpaceAuthenticationScheme authenticationScheme = ProtectionSpaceAuthenticationSchemeDefault;
890    if (serverType == ProtectionSpaceServerHTTP) {
891        if (!strcasecmp(scheme, "basic"))
892            authenticationScheme = ProtectionSpaceAuthenticationSchemeHTTPBasic;
893        else if (!strcmp(scheme, "digest"))
894            authenticationScheme = ProtectionSpaceAuthenticationSchemeHTTPDigest;
895    }
896
897    protectionSpace = ProtectionSpace(host, port, serverType, realm, authenticationScheme);
898    return true;
899}
900
901static NPError NPN_GetAuthenticationInfo(NPP npp, const char* protocol, const char* host, int32_t port, const char* scheme,
902                                         const char* realm, char** username, uint32_t* usernameLength, char** password, uint32_t* passwordLength)
903{
904    if (!protocol || !host || !scheme || !realm || !username || !usernameLength || !password || !passwordLength)
905        return NPERR_GENERIC_ERROR;
906
907    ProtectionSpace protectionSpace;
908    if (!initializeProtectionSpace(protocol, host, port, scheme, realm, protectionSpace))
909        return NPERR_GENERIC_ERROR;
910
911    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
912    String usernameString;
913    String passwordString;
914    if (!plugin->getAuthenticationInfo(protectionSpace, usernameString, passwordString))
915        return NPERR_GENERIC_ERROR;
916
917    NPError result = copyCString(usernameString.utf8(), username, usernameLength);
918    if (result != NPERR_NO_ERROR)
919        return result;
920
921    result = copyCString(passwordString.utf8(), password, passwordLength);
922    if (result != NPERR_NO_ERROR) {
923        npnMemFree(*username);
924        return result;
925    }
926
927    return NPERR_NO_ERROR;
928}
929
930static uint32_t NPN_ScheduleTimer(NPP npp, uint32_t interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32_t timerID))
931{
932    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
933
934    return plugin->scheduleTimer(interval, repeat, timerFunc);
935}
936
937static void NPN_UnscheduleTimer(NPP npp, uint32_t timerID)
938{
939    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
940
941    plugin->unscheduleTimer(timerID);
942}
943
944#if PLATFORM(COCOA)
945static NPError NPN_PopUpContextMenu(NPP npp, NPMenu* menu)
946{
947    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
948
949    return plugin->popUpContextMenu(menu);
950}
951
952static NPBool NPN_ConvertPoint(NPP npp, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double* destX, double* destY, NPCoordinateSpace destSpace)
953{
954    RefPtr<NetscapePlugin> plugin = NetscapePlugin::fromNPP(npp);
955
956    double destinationX;
957    double destinationY;
958
959    bool returnValue = plugin->convertPoint(sourceX, sourceY, sourceSpace, destinationX, destinationY, destSpace);
960
961    if (destX)
962        *destX = destinationX;
963    if (destY)
964        *destY = destinationY;
965
966    return returnValue;
967}
968#endif
969
970static void initializeBrowserFuncs(NPNetscapeFuncs &netscapeFuncs)
971{
972    netscapeFuncs.size = sizeof(NPNetscapeFuncs);
973    netscapeFuncs.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
974
975    netscapeFuncs.geturl = NPN_GetURL;
976    netscapeFuncs.posturl = NPN_PostURL;
977    netscapeFuncs.requestread = NPN_RequestRead;
978    netscapeFuncs.newstream = NPN_NewStream;
979    netscapeFuncs.write = NPN_Write;
980    netscapeFuncs.destroystream = NPN_DestroyStream;
981    netscapeFuncs.status = NPN_Status;
982    netscapeFuncs.uagent = NPN_UserAgent;
983    netscapeFuncs.memalloc = NPN_MemAlloc;
984    netscapeFuncs.memfree = NPN_MemFree;
985    netscapeFuncs.memflush = NPN_MemFlush;
986    netscapeFuncs.reloadplugins = NPN_ReloadPlugins;
987    netscapeFuncs.getJavaEnv = NPN_GetJavaEnv;
988    netscapeFuncs.getJavaPeer = NPN_GetJavaPeer;
989    netscapeFuncs.geturlnotify = NPN_GetURLNotify;
990    netscapeFuncs.posturlnotify = NPN_PostURLNotify;
991    netscapeFuncs.getvalue = NPN_GetValue;
992    netscapeFuncs.setvalue = NPN_SetValue;
993    netscapeFuncs.invalidaterect = NPN_InvalidateRect;
994    netscapeFuncs.invalidateregion = NPN_InvalidateRegion;
995    netscapeFuncs.forceredraw = NPN_ForceRedraw;
996
997    netscapeFuncs.getstringidentifier = NPN_GetStringIdentifier;
998    netscapeFuncs.getstringidentifiers = NPN_GetStringIdentifiers;
999    netscapeFuncs.getintidentifier = NPN_GetIntIdentifier;
1000    netscapeFuncs.identifierisstring = NPN_IdentifierIsString;
1001    netscapeFuncs.utf8fromidentifier = NPN_UTF8FromIdentifier;
1002    netscapeFuncs.intfromidentifier = NPN_IntFromIdentifier;
1003    netscapeFuncs.createobject = NPN_CreateObject;
1004    netscapeFuncs.retainobject = NPN_RetainObject;
1005    netscapeFuncs.releaseobject = NPN_ReleaseObject;
1006    netscapeFuncs.invoke = NPN_Invoke;
1007    netscapeFuncs.invokeDefault = NPN_InvokeDefault;
1008    netscapeFuncs.evaluate = NPN_Evaluate;
1009    netscapeFuncs.getproperty = NPN_GetProperty;
1010    netscapeFuncs.setproperty = NPN_SetProperty;
1011    netscapeFuncs.removeproperty = NPN_RemoveProperty;
1012    netscapeFuncs.hasproperty = NPN_HasProperty;
1013    netscapeFuncs.hasmethod = NPN_HasMethod;
1014    netscapeFuncs.releasevariantvalue = NPN_ReleaseVariantValue;
1015    netscapeFuncs.setexception = NPN_SetException;
1016    netscapeFuncs.pushpopupsenabledstate = NPN_PushPopupsEnabledState;
1017    netscapeFuncs.poppopupsenabledstate = NPN_PopPopupsEnabledState;
1018    netscapeFuncs.enumerate = NPN_Enumerate;
1019    netscapeFuncs.pluginthreadasynccall = NPN_PluginThreadAsyncCall;
1020    netscapeFuncs.construct = NPN_Construct;
1021    netscapeFuncs.getvalueforurl = NPN_GetValueForURL;
1022    netscapeFuncs.setvalueforurl = NPN_SetValueForURL;
1023    netscapeFuncs.getauthenticationinfo = NPN_GetAuthenticationInfo;
1024    netscapeFuncs.scheduletimer = NPN_ScheduleTimer;
1025    netscapeFuncs.unscheduletimer = NPN_UnscheduleTimer;
1026#if PLATFORM(COCOA)
1027    netscapeFuncs.popupcontextmenu = NPN_PopUpContextMenu;
1028    netscapeFuncs.convertpoint = NPN_ConvertPoint;
1029#else
1030    netscapeFuncs.popupcontextmenu = 0;
1031    netscapeFuncs.convertpoint = 0;
1032#endif
1033}
1034
1035NPNetscapeFuncs* netscapeBrowserFuncs()
1036{
1037    static NPNetscapeFuncs netscapeFuncs;
1038    static bool initialized = false;
1039
1040    if (!initialized) {
1041        initializeBrowserFuncs(netscapeFuncs);
1042        initialized = true;
1043    }
1044
1045    return &netscapeFuncs;
1046}
1047
1048} // namespace WebKit
1049
1050#endif // ENABLE(NETSCAPE_PLUGIN_API)
1051