1/*
2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2009 Holger Hans Peter Freyther
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "PluginObject.h"
28
29#include "TestObject.h"
30#include <assert.h>
31#include <stdarg.h>
32#include <stdio.h>
33
34#include <string.h>
35#include <stdlib.h>
36
37// Helper function which takes in the plugin window object for logging to the console object.
38static void pluginLogWithWindowObject(NPObject* windowObject, NPP instance, const char* message)
39{
40    NPVariant consoleVariant;
41    if (!browser->getproperty(instance, windowObject, browser->getstringidentifier("console"), &consoleVariant)) {
42        fprintf(stderr, "Failed to retrieve console object while logging: %s\n", message);
43        return;
44    }
45
46    NPObject* consoleObject = NPVARIANT_TO_OBJECT(consoleVariant);
47
48    NPVariant messageVariant;
49    STRINGZ_TO_NPVARIANT(message, messageVariant);
50
51    NPVariant result;
52    if (!browser->invoke(instance, consoleObject, browser->getstringidentifier("log"), &messageVariant, 1, &result)) {
53        fprintf(stderr, "Failed to invoke console.log while logging: %s\n", message);
54        browser->releaseobject(consoleObject);
55        return;
56    }
57
58    browser->releasevariantvalue(&result);
59    browser->releaseobject(consoleObject);
60}
61
62// Helper function which takes in the plugin window object for logging to the console object. This function supports variable
63// arguments.
64static void pluginLogWithWindowObjectVariableArgs(NPObject* windowObject, NPP instance, const char* format, ...)
65{
66    va_list args;
67    va_start(args, format);
68    char message[2048] = "PLUGIN: ";
69    vsprintf(message + strlen(message), format, args);
70    va_end(args);
71
72    pluginLogWithWindowObject(windowObject, instance, message);
73}
74
75// Helper function to log to the console object.
76void pluginLog(NPP instance, const char* format, ...)
77{
78    va_list args;
79    va_start(args, format);
80    char message[2048] = "PLUGIN: ";
81    vsprintf(message + strlen(message), format, args);
82    va_end(args);
83
84    NPObject* windowObject = 0;
85    NPError error = browser->getvalue(instance, NPNVWindowNPObject, &windowObject);
86    if (error != NPERR_NO_ERROR) {
87        fprintf(stderr, "Failed to retrieve window object while logging: %s\n", message);
88        return;
89    }
90
91    pluginLogWithWindowObject(windowObject, instance, message);
92    browser->releaseobject(windowObject);
93}
94
95static void pluginInvalidate(NPObject*);
96static bool pluginHasProperty(NPObject*, NPIdentifier name);
97static bool pluginHasMethod(NPObject*, NPIdentifier name);
98static bool pluginGetProperty(NPObject*, NPIdentifier name, NPVariant*);
99static bool pluginSetProperty(NPObject*, NPIdentifier name, const NPVariant*);
100static bool pluginInvoke(NPObject*, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result);
101static bool pluginInvokeDefault(NPObject*, const NPVariant* args, uint32_t argCount, NPVariant* result);
102static NPObject* pluginAllocate(NPP npp, NPClass*);
103static void pluginDeallocate(NPObject*);
104
105NPNetscapeFuncs* browser;
106
107static NPClass pluginClass = {
108    NP_CLASS_STRUCT_VERSION,
109    pluginAllocate,
110    pluginDeallocate,
111    pluginInvalidate,
112    pluginHasMethod,
113    pluginInvoke,
114    pluginInvokeDefault,
115    pluginHasProperty,
116    pluginGetProperty,
117    pluginSetProperty,
118};
119
120NPClass *getPluginClass(void)
121{
122    return &pluginClass;
123}
124
125static bool identifiersInitialized = false;
126
127enum {
128    ID_PROPERTY_PROPERTY = 0,
129    ID_PROPERTY_EVENT_LOGGING,
130    ID_PROPERTY_HAS_STREAM,
131    ID_PROPERTY_TEST_OBJECT,
132    ID_PROPERTY_LOG_DESTROY,
133    ID_PROPERTY_RETURN_ERROR_FROM_NEWSTREAM,
134    ID_PROPERTY_PRIVATE_BROWSING_ENABLED,
135    ID_PROPERTY_CACHED_PRIVATE_BROWSING_ENABLED,
136    ID_PROPERTY_THROW_EXCEPTION_PROPERTY,
137    NUM_PROPERTY_IDENTIFIERS
138};
139
140static NPIdentifier pluginPropertyIdentifiers[NUM_PROPERTY_IDENTIFIERS];
141static const NPUTF8 *pluginPropertyIdentifierNames[NUM_PROPERTY_IDENTIFIERS] = {
142    "property",
143    "eventLoggingEnabled",
144    "hasStream",
145    "testObject",
146    "logDestroy",
147    "returnErrorFromNewStream",
148    "privateBrowsingEnabled",
149    "cachedPrivateBrowsingEnabled",
150    "testThrowExceptionProperty"
151};
152
153enum {
154    ID_TEST_CALLBACK_METHOD = 0,
155    ID_TEST_GETURL,
156    ID_REMOVE_DEFAULT_METHOD,
157    ID_TEST_DOM_ACCESS,
158    ID_TEST_GET_URL_NOTIFY,
159    ID_TEST_INVOKE_DEFAULT,
160    ID_DESTROY_STREAM,
161    ID_TEST_ENUMERATE,
162    ID_TEST_GETINTIDENTIFIER,
163    ID_TEST_GET_PROPERTY,
164    ID_TEST_HAS_PROPERTY,
165    ID_TEST_HAS_METHOD,
166    ID_TEST_EVALUATE,
167    ID_TEST_GET_PROPERTY_RETURN_VALUE,
168    ID_TEST_IDENTIFIER_TO_STRING,
169    ID_TEST_IDENTIFIER_TO_INT,
170    ID_TEST_POSTURL_FILE,
171    ID_TEST_CONSTRUCT,
172    ID_TEST_THROW_EXCEPTION_METHOD,
173    ID_TEST_FAIL_METHOD,
174    ID_DESTROY_NULL_STREAM,
175    ID_TEST_RELOAD_PLUGINS_NO_PAGES,
176    ID_TEST_RELOAD_PLUGINS_AND_PAGES,
177    ID_TEST_GET_BROWSER_PROPERTY,
178    ID_TEST_SET_BROWSER_PROPERTY,
179    NUM_METHOD_IDENTIFIERS
180};
181
182static NPIdentifier pluginMethodIdentifiers[NUM_METHOD_IDENTIFIERS];
183static const NPUTF8 *pluginMethodIdentifierNames[NUM_METHOD_IDENTIFIERS] = {
184    "testCallback",
185    "getURL",
186    "removeDefaultMethod",
187    "testDOMAccess",
188    "getURLNotify",
189    "testInvokeDefault",
190    "destroyStream",
191    "testEnumerate",
192    "testGetIntIdentifier",
193    "testGetProperty",
194    "testHasProperty",
195    "testHasMethod",
196    "testEvaluate",
197    "testGetPropertyReturnValue",
198    "testIdentifierToString",
199    "testIdentifierToInt",
200    "testPostURLFile",
201    "testConstruct",
202    "testThrowException",
203    "testFail",
204    "destroyNullStream",
205    "reloadPluginsNoPages",
206    "reloadPluginsAndPages",
207    "testGetBrowserProperty",
208    "testSetBrowserProperty"
209};
210
211static NPUTF8* createCStringFromNPVariant(const NPVariant* variant)
212{
213    size_t length = NPVARIANT_TO_STRING(*variant).UTF8Length;
214    NPUTF8* result = (NPUTF8*)malloc(length + 1);
215    memcpy(result, NPVARIANT_TO_STRING(*variant).UTF8Characters, length);
216    result[length] = '\0';
217    return result;
218}
219
220static void initializeIdentifiers(void)
221{
222    browser->getstringidentifiers(pluginPropertyIdentifierNames, NUM_PROPERTY_IDENTIFIERS, pluginPropertyIdentifiers);
223    browser->getstringidentifiers(pluginMethodIdentifierNames, NUM_METHOD_IDENTIFIERS, pluginMethodIdentifiers);
224}
225
226static bool pluginHasProperty(NPObject *obj, NPIdentifier name)
227{
228    for (int i = 0; i < NUM_PROPERTY_IDENTIFIERS; i++)
229        if (name == pluginPropertyIdentifiers[i])
230            return true;
231    return false;
232}
233
234static bool pluginHasMethod(NPObject *obj, NPIdentifier name)
235{
236    for (int i = 0; i < NUM_METHOD_IDENTIFIERS; i++)
237        if (name == pluginMethodIdentifiers[i])
238            return true;
239    return false;
240}
241
242static bool pluginGetProperty(NPObject* obj, NPIdentifier name, NPVariant* result)
243{
244    PluginObject* plugin = reinterpret_cast<PluginObject*>(obj);
245    if (name == pluginPropertyIdentifiers[ID_PROPERTY_PROPERTY]) {
246        STRINGZ_TO_NPVARIANT("property", *result);
247        return true;
248    } else if (name == pluginPropertyIdentifiers[ID_PROPERTY_EVENT_LOGGING]) {
249        BOOLEAN_TO_NPVARIANT(plugin->eventLogging, *result);
250        return true;
251    } else if (name == pluginPropertyIdentifiers[ID_PROPERTY_LOG_DESTROY]) {
252        BOOLEAN_TO_NPVARIANT(plugin->logDestroy, *result);
253        return true;
254    } else if (name == pluginPropertyIdentifiers[ID_PROPERTY_HAS_STREAM]) {
255        BOOLEAN_TO_NPVARIANT(plugin->stream != 0, *result);
256        return true;
257    } else if (name == pluginPropertyIdentifiers[ID_PROPERTY_TEST_OBJECT]) {
258        NPObject* testObject = plugin->testObject;
259        browser->retainobject(testObject);
260        OBJECT_TO_NPVARIANT(testObject, *result);
261        return true;
262    } else if (name == pluginPropertyIdentifiers[ID_PROPERTY_RETURN_ERROR_FROM_NEWSTREAM]) {
263        BOOLEAN_TO_NPVARIANT(plugin->returnErrorFromNewStream, *result);
264        return true;
265    } else if (name == pluginPropertyIdentifiers[ID_PROPERTY_PRIVATE_BROWSING_ENABLED]) {
266        NPBool privateBrowsingEnabled = FALSE;
267        browser->getvalue(plugin->npp, NPNVprivateModeBool, &privateBrowsingEnabled);
268        BOOLEAN_TO_NPVARIANT(privateBrowsingEnabled, *result);
269        return true;
270    } else if (name == pluginPropertyIdentifiers[ID_PROPERTY_CACHED_PRIVATE_BROWSING_ENABLED]) {
271        BOOLEAN_TO_NPVARIANT(plugin->cachedPrivateBrowsingMode, *result);
272        return true;
273    } else if (name == pluginPropertyIdentifiers[ID_PROPERTY_THROW_EXCEPTION_PROPERTY]) {
274        browser->setexception(obj, "plugin object testThrowExceptionProperty SUCCESS");
275        return true;
276    }
277    return false;
278}
279
280static bool pluginSetProperty(NPObject* obj, NPIdentifier name, const NPVariant* variant)
281{
282    PluginObject* plugin = reinterpret_cast<PluginObject*>(obj);
283    if (name == pluginPropertyIdentifiers[ID_PROPERTY_EVENT_LOGGING]) {
284        plugin->eventLogging = NPVARIANT_TO_BOOLEAN(*variant);
285        return true;
286    } else if (name == pluginPropertyIdentifiers[ID_PROPERTY_LOG_DESTROY]) {
287        plugin->logDestroy = NPVARIANT_TO_BOOLEAN(*variant);
288        return true;
289    } else if (name == pluginPropertyIdentifiers[ID_PROPERTY_RETURN_ERROR_FROM_NEWSTREAM]) {
290        plugin->returnErrorFromNewStream = NPVARIANT_TO_BOOLEAN(*variant);
291        return true;
292    } else if (name == pluginPropertyIdentifiers[ID_PROPERTY_THROW_EXCEPTION_PROPERTY]) {
293        browser->setexception(obj, "plugin object testThrowExceptionProperty SUCCESS");
294        return true;
295    }
296
297    return false;
298}
299
300static bool testDOMAccess(PluginObject* obj, const NPVariant*, uint32_t, NPVariant* result)
301{
302    // Get plug-in's DOM element
303    NPObject* elementObject;
304    if (browser->getvalue(obj->npp, NPNVPluginElementNPObject, &elementObject) == NPERR_NO_ERROR) {
305        // Get style
306        NPVariant styleVariant;
307        NPIdentifier styleIdentifier = browser->getstringidentifier("style");
308        if (browser->getproperty(obj->npp, elementObject, styleIdentifier, &styleVariant) && NPVARIANT_IS_OBJECT(styleVariant)) {
309            // Set style.border
310            NPIdentifier borderIdentifier = browser->getstringidentifier("border");
311            NPVariant borderVariant;
312            STRINGZ_TO_NPVARIANT("3px solid red", borderVariant);
313            browser->setproperty(obj->npp, NPVARIANT_TO_OBJECT(styleVariant), borderIdentifier, &borderVariant);
314            browser->releasevariantvalue(&styleVariant);
315        }
316
317        browser->releaseobject(elementObject);
318    }
319    VOID_TO_NPVARIANT(*result);
320    return true;
321}
322
323static NPIdentifier stringVariantToIdentifier(NPVariant variant)
324{
325    assert(NPVARIANT_IS_STRING(variant));
326    NPUTF8* utf8String = createCStringFromNPVariant(&variant);
327    NPIdentifier identifier = browser->getstringidentifier(utf8String);
328    free(utf8String);
329    return identifier;
330}
331
332static NPIdentifier int32VariantToIdentifier(NPVariant variant)
333{
334    assert(NPVARIANT_IS_INT32(variant));
335    int32 integer = NPVARIANT_TO_INT32(variant);
336    return browser->getintidentifier(integer);
337}
338
339static NPIdentifier doubleVariantToIdentifier(NPVariant variant)
340{
341    assert(NPVARIANT_IS_DOUBLE(variant));
342    double value = NPVARIANT_TO_DOUBLE(variant);
343    // Sadly there is no "getdoubleidentifier"
344    int32 integer = static_cast<int32>(value);
345    return browser->getintidentifier(integer);
346}
347
348static NPIdentifier variantToIdentifier(NPVariant variant)
349{
350    if (NPVARIANT_IS_STRING(variant))
351        return stringVariantToIdentifier(variant);
352    else if (NPVARIANT_IS_INT32(variant))
353        return int32VariantToIdentifier(variant);
354    else if (NPVARIANT_IS_DOUBLE(variant))
355        return doubleVariantToIdentifier(variant);
356    return 0;
357}
358
359static bool testIdentifierToString(PluginObject*, const NPVariant* args, uint32_t argCount, NPVariant* result)
360{
361    if (argCount != 1)
362        return true;
363    NPIdentifier identifier = variantToIdentifier(args[0]);
364    if (!identifier)
365        return true;
366    NPUTF8* utf8String = browser->utf8fromidentifier(identifier);
367    if (!utf8String)
368        return true;
369    STRINGZ_TO_NPVARIANT(utf8String, *result);
370    return true;
371}
372
373static bool testIdentifierToInt(PluginObject*, const NPVariant* args, uint32_t argCount, NPVariant* result)
374{
375    if (argCount != 1)
376        return false;
377    NPIdentifier identifier = variantToIdentifier(args[0]);
378    if (!identifier)
379        return false;
380    int32 integer = browser->intfromidentifier(identifier);
381    INT32_TO_NPVARIANT(integer, *result);
382    return true;
383}
384
385static bool testCallback(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
386{
387    if (argCount == 0 || !NPVARIANT_IS_STRING(args[0]))
388        return false;
389
390    NPObject* windowScriptObject;
391    browser->getvalue(obj->npp, NPNVWindowNPObject, &windowScriptObject);
392
393    NPUTF8* callbackString = createCStringFromNPVariant(&args[0]);
394    NPIdentifier callbackIdentifier = browser->getstringidentifier(callbackString);
395    free(callbackString);
396
397    NPVariant browserResult;
398    browser->invoke(obj->npp, windowScriptObject, callbackIdentifier, 0, 0, &browserResult);
399    browser->releasevariantvalue(&browserResult);
400
401    browser->releaseobject(windowScriptObject);
402
403    VOID_TO_NPVARIANT(*result);
404    return true;
405}
406
407static bool getURL(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
408{
409    if (argCount == 2 && NPVARIANT_IS_STRING(args[0]) && NPVARIANT_IS_STRING(args[1])) {
410        NPUTF8* urlString = createCStringFromNPVariant(&args[0]);
411        NPUTF8* targetString = createCStringFromNPVariant(&args[1]);
412        NPError npErr = browser->geturl(obj->npp, urlString, targetString);
413        free(urlString);
414        free(targetString);
415
416        INT32_TO_NPVARIANT(npErr, *result);
417        return true;
418    } else if (argCount == 1 && NPVARIANT_IS_STRING(args[0])) {
419        NPUTF8* urlString = createCStringFromNPVariant(&args[0]);
420        NPError npErr = browser->geturl(obj->npp, urlString, 0);
421        free(urlString);
422
423        INT32_TO_NPVARIANT(npErr, *result);
424        return true;
425    }
426    return false;
427}
428
429static bool removeDefaultMethod(PluginObject*, const NPVariant* args, uint32_t argCount, NPVariant* result)
430{
431    pluginClass.invokeDefault = 0;
432    VOID_TO_NPVARIANT(*result);
433    return true;
434}
435
436static bool getURLNotify(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
437{
438    if (argCount != 3 || !NPVARIANT_IS_STRING(args[0])
439        || (!NPVARIANT_IS_STRING(args[1]) && !NPVARIANT_IS_NULL(args[1]))
440        || !NPVARIANT_IS_STRING(args[2]))
441        return false;
442
443    NPUTF8* urlString = createCStringFromNPVariant(&args[0]);
444    NPUTF8* targetString = (NPVARIANT_IS_STRING(args[1]) ? createCStringFromNPVariant(&args[1]) : NULL);
445    NPUTF8* callbackString = createCStringFromNPVariant(&args[2]);
446
447    NPIdentifier callbackIdentifier = browser->getstringidentifier(callbackString);
448    browser->geturlnotify(obj->npp, urlString, targetString, callbackIdentifier);
449
450    free(urlString);
451    free(targetString);
452    free(callbackString);
453
454    VOID_TO_NPVARIANT(*result);
455    return true;
456}
457
458static bool testInvokeDefault(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
459{
460    if (!NPVARIANT_IS_OBJECT(args[0]))
461        return false;
462
463    NPObject *callback = NPVARIANT_TO_OBJECT(args[0]);
464
465    NPVariant invokeArgs[1];
466    NPVariant browserResult;
467
468    STRINGZ_TO_NPVARIANT("test", invokeArgs[0]);
469    bool retval = browser->invokeDefault(obj->npp, callback, invokeArgs, 1, &browserResult);
470
471    if (retval)
472        browser->releasevariantvalue(&browserResult);
473
474    BOOLEAN_TO_NPVARIANT(retval, *result);
475    return true;
476}
477
478static bool destroyStream(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
479{
480    NPError npError = browser->destroystream(obj->npp, obj->stream, NPRES_USER_BREAK);
481    INT32_TO_NPVARIANT(npError, *result);
482    return true;
483}
484
485static bool destroyNullStream(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
486{
487    NPError npError = browser->destroystream(obj->npp, 0, NPRES_USER_BREAK);
488    INT32_TO_NPVARIANT(npError, *result);
489    return true;
490}
491
492static bool testEnumerate(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
493{
494    if (argCount != 2 || !NPVARIANT_IS_OBJECT(args[0]) || !NPVARIANT_IS_OBJECT(args[1]))
495        return false;
496
497    uint32_t count;
498    NPIdentifier* identifiers;
499    if (browser->enumerate(obj->npp, NPVARIANT_TO_OBJECT(args[0]), &identifiers, &count)) {
500        NPObject* outArray = NPVARIANT_TO_OBJECT(args[1]);
501        NPIdentifier pushIdentifier = browser->getstringidentifier("push");
502
503        for (uint32_t i = 0; i < count; i++) {
504            NPUTF8* string = browser->utf8fromidentifier(identifiers[i]);
505
506            if (!string)
507                continue;
508
509            NPVariant args[1];
510            STRINGZ_TO_NPVARIANT(string, args[0]);
511            NPVariant browserResult;
512            browser->invoke(obj->npp, outArray, pushIdentifier, args, 1, &browserResult);
513            browser->releasevariantvalue(&browserResult);
514            browser->memfree(string);
515        }
516
517        browser->memfree(identifiers);
518    }
519
520    VOID_TO_NPVARIANT(*result);
521    return true;
522}
523
524static bool testGetIntIdentifier(PluginObject*, const NPVariant* args, uint32_t argCount, NPVariant* result)
525{
526    if (argCount != 1 || !NPVARIANT_IS_DOUBLE(args[0]))
527        return false;
528
529    NPIdentifier identifier = browser->getintidentifier((int)NPVARIANT_TO_DOUBLE(args[0]));
530    INT32_TO_NPVARIANT((int32)(long long)identifier, *result);
531    return true;
532}
533
534static bool testGetProperty(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
535{
536    if (argCount == 0)
537        return false;
538
539    NPObject *object;
540    browser->getvalue(obj->npp, NPNVWindowNPObject, &object);
541
542    for (uint32_t i = 0; i < argCount; i++) {
543        assert(NPVARIANT_IS_STRING(args[i]));
544        NPUTF8* propertyString = createCStringFromNPVariant(&args[i]);
545        NPIdentifier propertyIdentifier = browser->getstringidentifier(propertyString);
546        free(propertyString);
547
548        NPVariant variant;
549        bool retval = browser->getproperty(obj->npp, object, propertyIdentifier, &variant);
550        browser->releaseobject(object);
551
552        if (!retval)
553            break;
554
555        if (i + 1 < argCount) {
556            assert(NPVARIANT_IS_OBJECT(variant));
557            object = NPVARIANT_TO_OBJECT(variant);
558        } else {
559            *result = variant;
560            return true;
561        }
562    }
563
564    VOID_TO_NPVARIANT(*result);
565    return false;
566}
567
568static bool testHasProperty(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
569{
570    if (argCount != 2 || !NPVARIANT_IS_OBJECT(args[0]) || !NPVARIANT_IS_STRING(args[1]))
571        return false;
572
573    NPUTF8* propertyString = createCStringFromNPVariant(&args[1]);
574    NPIdentifier propertyIdentifier = browser->getstringidentifier(propertyString);
575    free(propertyString);
576
577    bool retval = browser->hasproperty(obj->npp, NPVARIANT_TO_OBJECT(args[0]), propertyIdentifier);
578
579    BOOLEAN_TO_NPVARIANT(retval, *result);
580    return true;
581}
582
583static bool testHasMethod(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
584{
585    if (argCount != 2 || !NPVARIANT_IS_OBJECT(args[0]) || !NPVARIANT_IS_STRING(args[1]))
586        return false;
587
588    NPUTF8* propertyString = createCStringFromNPVariant(&args[1]);
589    NPIdentifier propertyIdentifier = browser->getstringidentifier(propertyString);
590    free(propertyString);
591
592    bool retval = browser->hasmethod(obj->npp, NPVARIANT_TO_OBJECT(args[0]), propertyIdentifier);
593
594    BOOLEAN_TO_NPVARIANT(retval, *result);
595    return true;
596}
597
598static bool testEvaluate(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
599{
600    if (argCount != 1 || !NPVARIANT_IS_STRING(args[0]))
601        return false;
602    NPObject* windowScriptObject;
603    browser->getvalue(obj->npp, NPNVWindowNPObject, &windowScriptObject);
604
605    NPString s = NPVARIANT_TO_STRING(args[0]);
606
607    bool retval = browser->evaluate(obj->npp, windowScriptObject, &s, result);
608    browser->releaseobject(windowScriptObject);
609    return retval;
610}
611
612static bool testGetPropertyReturnValue(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
613{
614    if (argCount != 2 || !NPVARIANT_IS_OBJECT(args[0]) || !NPVARIANT_IS_STRING(args[1]))
615        return false;
616
617    NPUTF8* propertyString = createCStringFromNPVariant(&args[1]);
618    NPIdentifier propertyIdentifier = browser->getstringidentifier(propertyString);
619    free(propertyString);
620
621    NPVariant variant;
622    bool retval = browser->getproperty(obj->npp, NPVARIANT_TO_OBJECT(args[0]), propertyIdentifier, &variant);
623    if (retval)
624        browser->releasevariantvalue(&variant);
625
626    BOOLEAN_TO_NPVARIANT(retval, *result);
627    return true;
628}
629
630static char* toCString(const NPString& string)
631{
632    char* result = static_cast<char*>(malloc(string.UTF8Length + 1));
633    memcpy(result, string.UTF8Characters, string.UTF8Length);
634    result[string.UTF8Length] = '\0';
635
636    return result;
637}
638
639static bool testPostURLFile(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
640{
641    if (argCount != 4 || !NPVARIANT_IS_STRING(args[0]) || !NPVARIANT_IS_STRING(args[1]) || !NPVARIANT_IS_STRING(args[2]) || !NPVARIANT_IS_STRING(args[3]))
642        return false;
643
644    NPString urlString = NPVARIANT_TO_STRING(args[0]);
645    char* url = toCString(urlString);
646
647    NPString targetString = NPVARIANT_TO_STRING(args[1]);
648    char* target = toCString(targetString);
649
650    NPString pathString = NPVARIANT_TO_STRING(args[2]);
651    char* path = toCString(pathString);
652
653    NPString contentsString = NPVARIANT_TO_STRING(args[3]);
654
655    FILE* tempFile = fopen(path, "w");
656    if (!tempFile)
657        return false;
658
659    fwrite(contentsString.UTF8Characters, contentsString.UTF8Length, 1, tempFile);
660    fclose(tempFile);
661
662    NPError error = browser->posturl(obj->npp, url, target, pathString.UTF8Length, path, TRUE);
663
664    free(path);
665    free(target);
666    free(url);
667
668    BOOLEAN_TO_NPVARIANT(error == NPERR_NO_ERROR, *result);
669    return true;
670}
671
672static bool testConstruct(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
673{
674    if (!argCount || !NPVARIANT_IS_OBJECT(args[0]))
675        return false;
676
677    return browser->construct(obj->npp, NPVARIANT_TO_OBJECT(args[0]), args + 1, argCount - 1, result);
678}
679
680// Helper function to notify the layout test controller that the test completed.
681void notifyTestCompletion(NPP npp, NPObject* object)
682{
683    NPVariant result;
684    NPString script;
685    script.UTF8Characters = "javascript:window.layoutTestController.notifyDone();";
686    script.UTF8Length = strlen("javascript:window.layoutTestController.notifyDone();");
687    browser->evaluate(npp, object, &script, &result);
688    browser->releasevariantvalue(&result);
689}
690
691bool testDocumentOpen(NPP npp)
692{
693    NPIdentifier documentId = browser->getstringidentifier("document");
694    NPIdentifier openId = browser->getstringidentifier("open");
695
696    NPObject *windowObject = NULL;
697    browser->getvalue(npp, NPNVWindowNPObject, &windowObject);
698    if (!windowObject)
699        return false;
700
701    NPVariant docVariant;
702    browser->getproperty(npp, windowObject, documentId, &docVariant);
703    if (docVariant.type != NPVariantType_Object)
704        return false;
705
706    NPObject *documentObject = NPVARIANT_TO_OBJECT(docVariant);
707
708    NPVariant openArgs[2];
709    STRINGZ_TO_NPVARIANT("text/html", openArgs[0]);
710    STRINGZ_TO_NPVARIANT("_blank", openArgs[1]);
711
712    NPVariant result;
713    browser->invoke(npp, documentObject, openId, openArgs, 2, &result);
714    browser->releaseobject(documentObject);
715
716    if (result.type == NPVariantType_Object) {
717        pluginLogWithWindowObjectVariableArgs(windowObject, npp, "DOCUMENT OPEN SUCCESS");
718        notifyTestCompletion(npp, result.value.objectValue);
719        browser->releaseobject(result.value.objectValue);
720        return true;
721    }
722
723    return false;
724}
725
726bool testWindowOpen(NPP npp)
727{
728    NPIdentifier openId = browser->getstringidentifier("open");
729
730    NPObject *windowObject = NULL;
731    browser->getvalue(npp, NPNVWindowNPObject, &windowObject);
732    if (!windowObject)
733        return false;
734
735    NPVariant openArgs[2];
736    STRINGZ_TO_NPVARIANT("about:blank", openArgs[0]);
737    STRINGZ_TO_NPVARIANT("_blank", openArgs[1]);
738
739    NPVariant result;
740    browser->invoke(npp, windowObject, openId, openArgs, 2, &result);
741    if (result.type == NPVariantType_Object) {
742        pluginLogWithWindowObjectVariableArgs(windowObject, npp, "WINDOW OPEN SUCCESS");
743        notifyTestCompletion(npp, result.value.objectValue);
744        browser->releaseobject(result.value.objectValue);
745        return true;
746    }
747    return false;
748}
749
750static bool pluginInvoke(NPObject* header, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result)
751{
752    PluginObject* plugin = reinterpret_cast<PluginObject*>(header);
753    if (name == pluginMethodIdentifiers[ID_TEST_CALLBACK_METHOD])
754        return testCallback(plugin, args, argCount, result);
755    else if (name == pluginMethodIdentifiers[ID_TEST_GETURL])
756        return getURL(plugin, args, argCount, result);
757    else if (name == pluginMethodIdentifiers[ID_REMOVE_DEFAULT_METHOD])
758        return removeDefaultMethod(plugin, args, argCount, result);
759    else if (name == pluginMethodIdentifiers[ID_TEST_DOM_ACCESS])
760        return testDOMAccess(plugin, args, argCount, result);
761    else if (name == pluginMethodIdentifiers[ID_TEST_GET_URL_NOTIFY])
762        return getURLNotify(plugin, args, argCount, result);
763    else if (name == pluginMethodIdentifiers[ID_TEST_INVOKE_DEFAULT])
764        return testInvokeDefault(plugin, args, argCount, result);
765    else if (name == pluginMethodIdentifiers[ID_TEST_ENUMERATE])
766        return testEnumerate(plugin, args, argCount, result);
767    else if (name == pluginMethodIdentifiers[ID_DESTROY_STREAM])
768        return destroyStream(plugin, args, argCount, result);
769    else if (name == pluginMethodIdentifiers[ID_TEST_GETINTIDENTIFIER])
770        return testGetIntIdentifier(plugin, args, argCount, result);
771    else if (name == pluginMethodIdentifiers[ID_TEST_EVALUATE])
772        return testEvaluate(plugin, args, argCount, result);
773    else if (name == pluginMethodIdentifiers[ID_TEST_GET_PROPERTY])
774        return testGetProperty(plugin, args, argCount, result);
775    else if (name == pluginMethodIdentifiers[ID_TEST_GET_PROPERTY_RETURN_VALUE])
776        return testGetPropertyReturnValue(plugin, args, argCount, result);
777    else if (name == pluginMethodIdentifiers[ID_TEST_HAS_PROPERTY])
778        return testHasProperty(plugin, args, argCount, result);
779    else if (name == pluginMethodIdentifiers[ID_TEST_HAS_METHOD])
780        return testHasMethod(plugin, args, argCount, result);
781    else if (name == pluginMethodIdentifiers[ID_TEST_IDENTIFIER_TO_STRING])
782        return testIdentifierToString(plugin, args, argCount, result);
783    else if (name == pluginMethodIdentifiers[ID_TEST_IDENTIFIER_TO_INT])
784        return testIdentifierToInt(plugin, args, argCount, result);
785    else if (name == pluginMethodIdentifiers[ID_TEST_POSTURL_FILE])
786        return testPostURLFile(plugin, args, argCount, result);
787    else if (name == pluginMethodIdentifiers[ID_TEST_CONSTRUCT])
788        return testConstruct(plugin, args, argCount, result);
789    else if (name == pluginMethodIdentifiers[ID_TEST_THROW_EXCEPTION_METHOD]) {
790        browser->setexception(header, "plugin object testThrowException SUCCESS");
791        return true;
792    } else if (name == pluginMethodIdentifiers[ID_TEST_FAIL_METHOD]) {
793        NPObject* windowScriptObject;
794        browser->getvalue(plugin->npp, NPNVWindowNPObject, &windowScriptObject);
795        browser->invoke(plugin->npp, windowScriptObject, name, args, argCount, result);
796    } else if (name == pluginMethodIdentifiers[ID_DESTROY_NULL_STREAM])
797        return destroyNullStream(plugin, args, argCount, result);
798    else if (name == pluginMethodIdentifiers[ID_TEST_RELOAD_PLUGINS_NO_PAGES]) {
799        browser->reloadplugins(false);
800        return true;
801    } else if (name == pluginMethodIdentifiers[ID_TEST_RELOAD_PLUGINS_AND_PAGES]) {
802        browser->reloadplugins(true);
803        return true;
804    } else if (name == pluginMethodIdentifiers[ID_TEST_GET_BROWSER_PROPERTY]) {
805        browser->getproperty(plugin->npp, NPVARIANT_TO_OBJECT(args[0]), stringVariantToIdentifier(args[1]), result);
806        return true;
807    } else if (name == pluginMethodIdentifiers[ID_TEST_SET_BROWSER_PROPERTY]) {
808        browser->setproperty(plugin->npp, NPVARIANT_TO_OBJECT(args[0]), stringVariantToIdentifier(args[1]), &args[2]);
809        return true;
810    }
811
812    return false;
813}
814
815static bool pluginInvokeDefault(NPObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result)
816{
817    INT32_TO_NPVARIANT(1, *result);
818    return true;
819}
820
821static void pluginInvalidate(NPObject* header)
822{
823    PluginObject* plugin = reinterpret_cast<PluginObject*>(header);
824    plugin->testObject = 0;
825}
826
827static NPObject *pluginAllocate(NPP npp, NPClass *theClass)
828{
829    PluginObject* newInstance = (PluginObject*)malloc(sizeof(PluginObject));
830
831    if (!identifiersInitialized) {
832        identifiersInitialized = true;
833        initializeIdentifiers();
834    }
835
836    newInstance->npp = npp;
837    newInstance->testObject = browser->createobject(npp, getTestClass());
838    newInstance->eventLogging = FALSE;
839    newInstance->onStreamLoad = 0;
840    newInstance->onStreamDestroy = 0;
841    newInstance->onDestroy = 0;
842    newInstance->onURLNotify = 0;
843    newInstance->logDestroy = FALSE;
844    newInstance->logSetWindow = FALSE;
845    newInstance->returnErrorFromNewStream = FALSE;
846    newInstance->stream = 0;
847
848    newInstance->firstUrl = NULL;
849    newInstance->firstHeaders = NULL;
850    newInstance->lastUrl = NULL;
851    newInstance->lastHeaders = NULL;
852
853    newInstance->testDocumentOpenInDestroyStream = FALSE;
854    newInstance->testWindowOpen = FALSE;
855
856    return (NPObject*)newInstance;
857}
858
859static void pluginDeallocate(NPObject* header)
860{
861    PluginObject* plugin = reinterpret_cast<PluginObject*>(header);
862    if (plugin->testObject)
863        browser->releaseobject(plugin->testObject);
864
865    free(plugin->firstUrl);
866    free(plugin->firstHeaders);
867    free(plugin->lastUrl);
868    free(plugin->lastHeaders);
869    free(plugin);
870}
871
872void handleCallback(PluginObject* object, const char *url, NPReason reason, void *notifyData)
873{
874    assert(object);
875
876    NPVariant args[2];
877
878    NPObject *windowScriptObject;
879    browser->getvalue(object->npp, NPNVWindowNPObject, &windowScriptObject);
880
881    NPIdentifier callbackIdentifier = notifyData;
882
883    INT32_TO_NPVARIANT(reason, args[0]);
884
885    char *strHdr = NULL;
886    if (object->firstUrl && object->firstHeaders && object->lastUrl && object->lastHeaders) {
887        // Format expected by JavaScript validator: four fields separated by \n\n:
888        // First URL; first header block; last URL; last header block.
889        // Note that header blocks already end with \n due to how NPStream::headers works.
890        int len = strlen(object->firstUrl) + 2
891            + strlen(object->firstHeaders) + 1
892            + strlen(object->lastUrl) + 2
893            + strlen(object->lastHeaders) + 1;
894        strHdr = (char*)malloc(len + 1);
895        snprintf(strHdr, len + 1, "%s\n\n%s\n%s\n\n%s\n",
896                 object->firstUrl, object->firstHeaders, object->lastUrl, object->lastHeaders);
897        STRINGN_TO_NPVARIANT(strHdr, len, args[1]);
898    } else
899        NULL_TO_NPVARIANT(args[1]);
900
901    NPVariant browserResult;
902    browser->invoke(object->npp, windowScriptObject, callbackIdentifier, args, 2, &browserResult);
903    browser->releasevariantvalue(&browserResult);
904
905    free(strHdr);
906}
907
908void notifyStream(PluginObject* object, const char *url, const char *headers)
909{
910    if (object->firstUrl == NULL) {
911        if (url)
912            object->firstUrl = strdup(url);
913        if (headers)
914            object->firstHeaders = strdup(headers);
915    } else {
916        free(object->lastUrl);
917        free(object->lastHeaders);
918        object->lastUrl = (url ? strdup(url) : NULL);
919        object->lastHeaders = (headers ? strdup(headers) : NULL);
920    }
921}
922
923void testNPRuntime(NPP npp)
924{
925    NPObject* windowScriptObject;
926    browser->getvalue(npp, NPNVWindowNPObject, &windowScriptObject);
927
928    // Invoke
929    NPIdentifier testNPInvoke = browser->getstringidentifier("testNPInvoke");
930    NPVariant args[7];
931
932    VOID_TO_NPVARIANT(args[0]);
933    NULL_TO_NPVARIANT(args[1]);
934    BOOLEAN_TO_NPVARIANT(true, args[2]);
935    INT32_TO_NPVARIANT(242, args[3]);
936    DOUBLE_TO_NPVARIANT(242.242, args[4]);
937    STRINGZ_TO_NPVARIANT("Hello, World", args[5]);
938    OBJECT_TO_NPVARIANT(windowScriptObject, args[6]);
939
940    NPVariant result;
941    if (browser->invoke(npp, windowScriptObject, testNPInvoke, args, 7, &result))
942        browser->releasevariantvalue(&result);
943
944    browser->releaseobject(windowScriptObject);
945}
946