1/*
2 * Copyright (C) 2014 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 "PluginTest.h"
27
28#include <string.h>
29
30using namespace std;
31
32class URLRedirect : public PluginTest {
33public:
34 URLRedirect(NPP npp, const string& identifier)
35 : PluginTest(npp, identifier)
36 {
37 }
38
39 struct Redirect {
40 int redirectsRemaining;
41 bool async;
42 bool hasFired;
43 };
44
45 std::map<void*, Redirect> redirects;
46
47private:
48 // This is the test object.
49 class TestObject : public Object<TestObject> { };
50
51 // This is the scriptable object. It has a single "testObject" property and an "evaluate" function.
52 class ScriptableObject : public Object<ScriptableObject> {
53 public:
54 bool hasMethod(NPIdentifier methodName)
55 {
56 return identifierIs(methodName, "get") || identifierIs(methodName, "getAsync") || identifierIs(methodName, "serviceAsync");
57 }
58
59 bool get(const NPVariant* args, uint32_t argCount, NPVariant* result, bool async)
60 {
61 if (argCount != 3 || !NPVARIANT_IS_STRING(args[0]) || !(NPVARIANT_IS_BOOLEAN(args[1]) || NPVARIANT_IS_DOUBLE(args[1]) || NPVARIANT_IS_INT32(args[1])) || !NPVARIANT_IS_STRING(args[2]))
62 return false;
63
64 const NPString* notifyString = &NPVARIANT_TO_STRING(args[2]);
65 basic_string<NPUTF8> notify(notifyString->UTF8Characters, notifyString->UTF8Length);
66 NPIdentifier notifyMethod = pluginTest()->NPN_GetStringIdentifier(notify.c_str());
67
68 Redirect& redirect = static_cast<URLRedirect*>(pluginTest())->redirects[reinterpret_cast<void*>(notifyMethod)];
69 if (NPVARIANT_IS_DOUBLE(args[1]))
70 redirect.redirectsRemaining = NPVARIANT_TO_DOUBLE(args[1]);
71 else if (NPVARIANT_IS_INT32(args[1]))
72 redirect.redirectsRemaining = NPVARIANT_TO_INT32(args[1]);
73 else if (NPVARIANT_IS_BOOLEAN(args[1]))
74 redirect.redirectsRemaining = NPVARIANT_TO_BOOLEAN(args[1]);
75 redirect.async = async;
76 redirect.hasFired = true;
77
78 const NPString* urlString = &NPVARIANT_TO_STRING(args[0]);
79 basic_string<NPUTF8> url(urlString->UTF8Characters, urlString->UTF8Length);
80
81 pluginTest()->NPN_GetURLNotify(url.c_str(), 0, reinterpret_cast<void*>(notifyMethod));
82
83 VOID_TO_NPVARIANT(*result);
84 return true;
85 }
86
87 bool serviceAsync(const NPVariant* args, uint32_t argCount, NPVariant* result)
88 {
89 if (argCount)
90 return false;
91
92 NPBool seen = 0;
93 URLRedirect* plugin = static_cast<URLRedirect*>(pluginTest());
94 for (auto& redirect : plugin->redirects) {
95 if (redirect.second.hasFired)
96 continue;
97 redirect.second.hasFired = true;
98 plugin->NPN_URLRedirectResponse(redirect.first, redirect.second.redirectsRemaining);
99 if (redirect.second.redirectsRemaining)
100 --redirect.second.redirectsRemaining;
101 seen = 1;
102 }
103
104 BOOLEAN_TO_NPVARIANT(seen, *result);
105 return true;
106 }
107
108 bool invoke(NPIdentifier methodName, const NPVariant* args, uint32_t argCount, NPVariant* result)
109 {
110 if (identifierIs(methodName, "get"))
111 return get(args, argCount, result, false);
112
113 if (identifierIs(methodName, "getAsync"))
114 return get(args, argCount, result, true);
115
116 if (identifierIs(methodName, "serviceAsync"))
117 return serviceAsync(args, argCount, result);
118
119 return false;
120 }
121 };
122
123 virtual NPError NPP_GetValue(NPPVariable variable, void *value)
124 {
125 if (variable != NPPVpluginScriptableNPObject)
126 return NPERR_GENERIC_ERROR;
127
128 *(NPObject**)value = ScriptableObject::create(this);
129
130 return NPERR_NO_ERROR;
131 }
132
133 virtual bool NPP_URLNotify(const char* url, NPReason reason, void* notifyData)
134 {
135 NPVariant args[2];
136
137 NPObject* windowScriptObject;
138 NPN_GetValue(NPNVWindowNPObject, &windowScriptObject);
139
140 NPIdentifier callbackIdentifier = notifyData;
141
142 INT32_TO_NPVARIANT(reason, args[0]);
143 STRINGZ_TO_NPVARIANT(url, args[1]);
144
145 NPVariant browserResult;
146 if (NPN_Invoke(windowScriptObject, callbackIdentifier, args, 2, &browserResult))
147 NPN_ReleaseVariantValue(&browserResult);
148
149 return true;
150 }
151
152 virtual void NPP_URLRedirectNotify(const char*, int32_t, void* notifyData)
153 {
154 Redirect& redirect = redirects[notifyData];
155 if (redirect.async) {
156 redirect.hasFired = false;
157 return;
158 }
159
160 NPN_URLRedirectResponse(notifyData, redirect.redirectsRemaining);
161 if (redirect.redirectsRemaining)
162 --redirect.redirectsRemaining;
163 }
164};
165
166static PluginTest::Register<URLRedirect> urlRedirect("url-redirect");
167
168