1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/test/plugin/plugin_geturl_test.h"
6
7 #include <stdio.h>
8
9 #include "base/basictypes.h"
10 #include "base/file_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14
15 // url for "self". The %22%22 is to make a statement for javascript to
16 // evaluate and return.
17 #define SELF_URL "javascript:window.location+\"\""
18
19 // The identifier for the self url stream.
20 #define SELF_URL_STREAM_ID 1
21
22 // The identifier for the fetched url stream.
23 #define FETCHED_URL_STREAM_ID 2
24
25 // url for testing GetURL with a bogus URL.
26 #define BOGUS_URL "bogoproto:///x:/asdf.xysdhffieasdf.asdhj/"
27
28 // url for testing redirect notifications sent to plugins.
29 #define REDIRECT_SRC_URL \
30 "http://mock.http/npapi/plugin_read_page_redirect_src.html"
31
32 // The notification id for the redirect notification url that we will cancel.
33 #define REDIRECT_SRC_URL_NOTIFICATION_CANCEL_ID 4
34
35 // The notification id for the redirect notification url that we will accept.
36 #define REDIRECT_SRC_URL_NOTIFICATION_ALLOW_ID 5
37
38 // The identifier for the bogus url stream.
39 #define BOGUS_URL_STREAM_ID 3
40
41 // The maximum chunk size of stream data.
42 #define STREAM_CHUNK 197
43
44 namespace NPAPIClient {
45
PluginGetURLTest(NPP id,NPNetscapeFuncs * host_functions)46 PluginGetURLTest::PluginGetURLTest(NPP id, NPNetscapeFuncs *host_functions)
47 : PluginTest(id, host_functions),
48 tests_started_(false),
49 tests_in_progress_(0),
50 test_file_(NULL),
51 expect_404_response_(false),
52 npn_evaluate_context_(false),
53 handle_url_redirects_(false),
54 received_url_redirect_cancel_notification_(false),
55 received_url_redirect_allow_notification_(false),
56 check_cookies_(false) {
57 }
58
~PluginGetURLTest()59 PluginGetURLTest::~PluginGetURLTest() {}
60
New(uint16 mode,int16 argc,const char * argn[],const char * argv[],NPSavedData * saved)61 NPError PluginGetURLTest::New(uint16 mode, int16 argc, const char* argn[],
62 const char* argv[], NPSavedData* saved) {
63 const char* page_not_found_url = GetArgValue("page_not_found_url", argc,
64 argn, argv);
65 if (page_not_found_url) {
66 page_not_found_url_ = page_not_found_url;
67 expect_404_response_ = true;
68 }
69
70 const char* fail_write_url = GetArgValue("fail_write_url", argc,
71 argn, argv);
72 if (fail_write_url) {
73 fail_write_url_ = fail_write_url;
74 }
75
76 const char* referrer_target_url = GetArgValue("ref_target", argc,
77 argn, argv);
78 if (referrer_target_url) {
79 referrer_target_url_ = referrer_target_url;
80 }
81
82 if (!base::strcasecmp(GetArgValue("name", argc, argn, argv),
83 "geturlredirectnotify")) {
84 handle_url_redirects_ = true;
85 }
86
87 NPError error = PluginTest::New(mode, argc, argn, argv, saved);
88
89 // The above sets test_name().
90 if (test_name() == "cookies")
91 check_cookies_ = true;
92
93 return error;
94 }
95
SetWindow(NPWindow * pNPWindow)96 NPError PluginGetURLTest::SetWindow(NPWindow* pNPWindow) {
97 #if !defined(OS_MACOSX)
98 if (pNPWindow->window == NULL)
99 return NPERR_NO_ERROR;
100 #endif
101
102 if (!tests_started_) {
103 tests_started_ = true;
104
105 tests_in_progress_++;
106
107 if (expect_404_response_) {
108 HostFunctions()->geturl(id(), page_not_found_url_.c_str(), NULL);
109 return NPERR_NO_ERROR;
110 } else if (!fail_write_url_.empty()) {
111 HostFunctions()->geturl(id(), fail_write_url_.c_str(), NULL);
112 return NPERR_NO_ERROR;
113 } else if (!referrer_target_url_.empty()) {
114 HostFunctions()->pushpopupsenabledstate(id(), true);
115 HostFunctions()->geturl(id(), referrer_target_url_.c_str(), "_blank");
116 HostFunctions()->poppopupsenabledstate(id());
117 return NPERR_NO_ERROR;
118 } else if (handle_url_redirects_) {
119 HostFunctions()->geturlnotify(
120 id(), REDIRECT_SRC_URL, NULL,
121 reinterpret_cast<void*>(REDIRECT_SRC_URL_NOTIFICATION_CANCEL_ID));
122 return NPERR_NO_ERROR;
123 } else if (check_cookies_) {
124 HostFunctions()->geturlnotify(
125 id(),
126 "plugin_ref_target_page.html",
127 NULL,
128 reinterpret_cast<void*>(SELF_URL_STREAM_ID));
129 return NPERR_NO_ERROR;
130 }
131
132 std::string url = SELF_URL;
133 HostFunctions()->geturlnotify(id(), url.c_str(), NULL,
134 reinterpret_cast<void*>(SELF_URL_STREAM_ID));
135
136 tests_in_progress_++;
137 std::string bogus_url = BOGUS_URL;
138 HostFunctions()->geturlnotify(id(), bogus_url.c_str(), NULL,
139 reinterpret_cast<void*>(BOGUS_URL_STREAM_ID));
140 }
141 return NPERR_NO_ERROR;
142 }
143
NewStream(NPMIMEType type,NPStream * stream,NPBool seekable,uint16 * stype)144 NPError PluginGetURLTest::NewStream(NPMIMEType type, NPStream* stream,
145 NPBool seekable, uint16* stype) {
146 if (stream == NULL) {
147 SetError("NewStream got null stream");
148 return NPERR_INVALID_PARAM;
149 }
150
151 if (test_completed()) {
152 return PluginTest::NewStream(type, stream, seekable, stype);
153 }
154
155 if (!referrer_target_url_.empty()) {
156 return NPERR_NO_ERROR;
157 }
158
159 COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
160 cast_validity_check);
161
162 if (expect_404_response_) {
163 NPObject *window_obj = NULL;
164 HostFunctions()->getvalue(id(), NPNVWindowNPObject, &window_obj);
165 if (!window_obj) {
166 SetError("Failed to get NPObject for plugin instance2");
167 SignalTestCompleted();
168 return NPERR_NO_ERROR;
169 }
170
171 std::string script = "javascript:document.title=\"OK\"";
172 NPString script_string;
173 script_string.UTF8Characters = script.c_str();
174 script_string.UTF8Length = static_cast<unsigned int>(script.length());
175 NPVariant result_var;
176
177 npn_evaluate_context_ = true;
178 HostFunctions()->evaluate(id(), window_obj, &script_string, &result_var);
179 npn_evaluate_context_ = false;
180 return NPERR_NO_ERROR;
181 }
182
183 if (!fail_write_url_.empty() || check_cookies_) {
184 return NPERR_NO_ERROR;
185 }
186
187
188 unsigned long stream_id = reinterpret_cast<unsigned long>(
189 stream->notifyData);
190
191 switch (stream_id) {
192 case SELF_URL_STREAM_ID:
193 break;
194 case FETCHED_URL_STREAM_ID:
195 {
196 std::string filename = self_url_;
197 if (filename.find("file:///", 0) != 0) {
198 SetError("Test expects a file-url.");
199 break;
200 }
201
202 // TODO(evanm): use the net:: functions to convert file:// URLs to
203 // on-disk file paths. But it probably doesn't actually matter in
204 // this test.
205
206 #if defined(OS_WIN)
207 filename = filename.substr(8); // remove "file:///"
208 // Assume an ASCII path on Windows.
209 base::FilePath path = base::FilePath(base::ASCIIToWide(filename));
210 #else
211 filename = filename.substr(7); // remove "file://"
212 base::FilePath path = base::FilePath(filename);
213 #endif
214
215 test_file_ = base::OpenFile(path, "r");
216 if (!test_file_) {
217 SetError("Could not open source file");
218 }
219 }
220 break;
221 case BOGUS_URL_STREAM_ID:
222 SetError("Unexpected NewStream for BOGUS_URL");
223 break;
224 case REDIRECT_SRC_URL_NOTIFICATION_CANCEL_ID:
225 SetError("Should not redirect to URL when plugin denied it.");
226 break;
227 case REDIRECT_SRC_URL_NOTIFICATION_ALLOW_ID:
228 break;
229 default:
230 SetError("Unexpected NewStream callback");
231 break;
232 }
233 return NPERR_NO_ERROR;
234 }
235
WriteReady(NPStream * stream)236 int32 PluginGetURLTest::WriteReady(NPStream *stream) {
237 if (test_completed()) {
238 return PluginTest::WriteReady(stream);
239 }
240
241 if (!referrer_target_url_.empty() || check_cookies_) {
242 return STREAM_CHUNK;
243 }
244
245 COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
246 cast_validity_check);
247 unsigned long stream_id = reinterpret_cast<unsigned long>(
248 stream->notifyData);
249 if (stream_id == BOGUS_URL_STREAM_ID)
250 SetError("Received WriteReady for BOGUS_URL");
251
252 return STREAM_CHUNK;
253 }
254
Write(NPStream * stream,int32 offset,int32 len,void * buffer)255 int32 PluginGetURLTest::Write(NPStream *stream, int32 offset, int32 len,
256 void *buffer) {
257 if (test_completed()) {
258 return PluginTest::Write(stream, offset, len, buffer);
259 }
260
261 if (!fail_write_url_.empty()) {
262 SignalTestCompleted();
263 return -1;
264 }
265
266 if (!referrer_target_url_.empty() || check_cookies_) {
267 return len;
268 }
269
270 if (stream == NULL) {
271 SetError("Write got null stream");
272 return -1;
273 }
274 if (len < 0 || len > STREAM_CHUNK) {
275 SetError("Write got bogus stream chunk size");
276 return -1;
277 }
278
279 COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
280 cast_validity_check);
281 unsigned long stream_id = reinterpret_cast<unsigned long>(
282 stream->notifyData);
283 switch (stream_id) {
284 case SELF_URL_STREAM_ID:
285 self_url_.append(static_cast<char*>(buffer), len);
286 break;
287 case FETCHED_URL_STREAM_ID:
288 {
289 char read_buffer[STREAM_CHUNK];
290 int32 bytes =
291 static_cast<int32>(fread(read_buffer, 1, len, test_file_));
292 // Technically, fread could return fewer than len
293 // bytes. But this is not likely.
294 if (bytes != len)
295 SetError("Did not read correct bytelength from source file");
296 if (memcmp(read_buffer, buffer, len))
297 SetError("Content mismatch between data and source!");
298 }
299 break;
300 case BOGUS_URL_STREAM_ID:
301 SetError("Unexpected write callback for BOGUS_URL");
302 break;
303 case REDIRECT_SRC_URL_NOTIFICATION_ALLOW_ID:
304 break;
305 default:
306 SetError("Unexpected write callback");
307 break;
308 }
309 // Pretend that we took all the data.
310 return len;
311 }
312
313
DestroyStream(NPStream * stream,NPError reason)314 NPError PluginGetURLTest::DestroyStream(NPStream *stream, NPError reason) {
315 if (test_completed()) {
316 return PluginTest::DestroyStream(stream, reason);
317 }
318
319 if (stream == NULL) {
320 SetError("NewStream got null stream");
321 return NPERR_INVALID_PARAM;
322 }
323
324 COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
325 cast_validity_check);
326
327 if (expect_404_response_) {
328 if (npn_evaluate_context_) {
329 SetError("Received destroyStream in the context of NPN_Evaluate.");
330 }
331
332 SignalTestCompleted();
333 return NPERR_NO_ERROR;
334 }
335
336 if (!referrer_target_url_.empty()) {
337 return NPERR_NO_ERROR;
338 }
339
340 if (check_cookies_) {
341 SignalTestCompleted();
342 return NPERR_NO_ERROR;
343 }
344
345 unsigned long stream_id =
346 reinterpret_cast<unsigned long>(stream->notifyData);
347 switch (stream_id) {
348 case SELF_URL_STREAM_ID:
349 // don't care
350 break;
351 case REDIRECT_SRC_URL_NOTIFICATION_ALLOW_ID:
352 break;
353 case FETCHED_URL_STREAM_ID:
354 {
355 char read_buffer[STREAM_CHUNK];
356 size_t bytes = fread(read_buffer, 1, sizeof(read_buffer), test_file_);
357 if (bytes != 0)
358 SetError("Data and source mismatch on length");
359 base::CloseFile(test_file_);
360 }
361 break;
362 default:
363 SetError("Unexpected NewStream callback");
364 break;
365 }
366 return NPERR_NO_ERROR;
367 }
368
StreamAsFile(NPStream * stream,const char * fname)369 void PluginGetURLTest::StreamAsFile(NPStream* stream, const char* fname) {
370 if (stream == NULL) {
371 SetError("NewStream got null stream");
372 return;
373 }
374
375 COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
376 cast_validity_check);
377 unsigned long stream_id =
378 reinterpret_cast<unsigned long>(stream->notifyData);
379 switch (stream_id) {
380 case SELF_URL_STREAM_ID:
381 // don't care
382 break;
383 default:
384 SetError("Unexpected NewStream callback");
385 break;
386 }
387 }
388
URLNotify(const char * url,NPReason reason,void * data)389 void PluginGetURLTest::URLNotify(const char* url, NPReason reason, void* data) {
390 if (!tests_in_progress_) {
391 SetError("URLNotify received after tests completed");
392 return;
393 }
394
395 if (!url) {
396 SetError("URLNotify received NULL url");
397 return;
398 }
399
400 if (check_cookies_)
401 return;
402
403 COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(data), cast_validity_check);
404 unsigned long stream_id = reinterpret_cast<unsigned long>(data);
405 switch (stream_id) {
406 case SELF_URL_STREAM_ID:
407 if (strcmp(url, SELF_URL) != 0)
408 SetError("URLNotify reported incorrect url for SELF_URL");
409
410 // We have our stream url. Go fetch it.
411 HostFunctions()->geturlnotify(id(), self_url_.c_str(), NULL,
412 reinterpret_cast<void*>(FETCHED_URL_STREAM_ID));
413 break;
414 case FETCHED_URL_STREAM_ID:
415 if (!url || strcmp(url, self_url_.c_str()) != 0)
416 SetError("URLNotify reported incorrect url for FETCHED_URL");
417 tests_in_progress_--;
418 break;
419 case BOGUS_URL_STREAM_ID:
420 if (reason != NPRES_NETWORK_ERR) {
421 std::string err = "BOGUS_URL received unexpected URLNotify status: ";
422 err.append(base::IntToString(reason));
423 SetError(err);
424 }
425 tests_in_progress_--;
426 break;
427 case REDIRECT_SRC_URL_NOTIFICATION_CANCEL_ID: {
428 if (!received_url_redirect_cancel_notification_) {
429 SetError("Failed to receive URLRedirect notification for cancel");
430 }
431 if (reason != NPRES_NETWORK_ERR) {
432 SetError("Redirected URL didn't get canceled");
433 }
434 break;
435 }
436 case REDIRECT_SRC_URL_NOTIFICATION_ALLOW_ID: {
437 if (!received_url_redirect_allow_notification_) {
438 SetError("Failed to receive URLRedirect notification for allow");
439 }
440 if (reason != NPRES_DONE) {
441 SetError("Redirected URL didn't complete successfully");
442 }
443 tests_in_progress_--;
444 break;
445 }
446 default:
447 SetError("Unexpected NewStream callback");
448 break;
449 }
450
451 if (tests_in_progress_ == 0)
452 SignalTestCompleted();
453 }
454
URLRedirectNotify(const char * url,int32_t status,void * notify_data)455 void PluginGetURLTest::URLRedirectNotify(const char* url,
456 int32_t status,
457 void* notify_data) {
458 unsigned long stream_id = reinterpret_cast<unsigned long>(notify_data);
459 if (stream_id == REDIRECT_SRC_URL_NOTIFICATION_CANCEL_ID) {
460 if (!base::strcasecmp(url,
461 "http://mock.http/npapi/plugin_read_page.html")) {
462 received_url_redirect_cancel_notification_ = true;
463 // Disallow redirect notification.
464 HostFunctions()->urlredirectresponse(id(), notify_data, false);
465
466 // Now start a request that we will allow to redirect.
467 HostFunctions()->geturlnotify(
468 id(), REDIRECT_SRC_URL, NULL,
469 reinterpret_cast<void*>(REDIRECT_SRC_URL_NOTIFICATION_ALLOW_ID));
470 }
471 } else if (stream_id == REDIRECT_SRC_URL_NOTIFICATION_ALLOW_ID) {
472 received_url_redirect_allow_notification_ = true;
473 HostFunctions()->urlredirectresponse(id(), notify_data, true);
474 }
475 }
476
477 } // namespace NPAPIClient
478