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