// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/test/plugin/plugin_geturl_test.h" #include <stdio.h> #include "base/basictypes.h" #include "base/file_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" // url for "self". The %22%22 is to make a statement for javascript to // evaluate and return. #define SELF_URL "javascript:window.location+\"\"" // The identifier for the self url stream. #define SELF_URL_STREAM_ID 1 // The identifier for the fetched url stream. #define FETCHED_URL_STREAM_ID 2 // url for testing GetURL with a bogus URL. #define BOGUS_URL "bogoproto:///x:/asdf.xysdhffieasdf.asdhj/" // url for testing redirect notifications sent to plugins. #define REDIRECT_SRC_URL \ "http://mock.http/npapi/plugin_read_page_redirect_src.html" // The notification id for the redirect notification url that we will cancel. #define REDIRECT_SRC_URL_NOTIFICATION_CANCEL_ID 4 // The notification id for the redirect notification url that we will accept. #define REDIRECT_SRC_URL_NOTIFICATION_ALLOW_ID 5 // The identifier for the bogus url stream. #define BOGUS_URL_STREAM_ID 3 // The maximum chunk size of stream data. #define STREAM_CHUNK 197 namespace NPAPIClient { PluginGetURLTest::PluginGetURLTest(NPP id, NPNetscapeFuncs *host_functions) : PluginTest(id, host_functions), tests_started_(false), tests_in_progress_(0), test_file_(NULL), expect_404_response_(false), npn_evaluate_context_(false), handle_url_redirects_(false), received_url_redirect_cancel_notification_(false), received_url_redirect_allow_notification_(false), check_cookies_(false) { } PluginGetURLTest::~PluginGetURLTest() {} NPError PluginGetURLTest::New(uint16 mode, int16 argc, const char* argn[], const char* argv[], NPSavedData* saved) { const char* page_not_found_url = GetArgValue("page_not_found_url", argc, argn, argv); if (page_not_found_url) { page_not_found_url_ = page_not_found_url; expect_404_response_ = true; } const char* fail_write_url = GetArgValue("fail_write_url", argc, argn, argv); if (fail_write_url) { fail_write_url_ = fail_write_url; } const char* referrer_target_url = GetArgValue("ref_target", argc, argn, argv); if (referrer_target_url) { referrer_target_url_ = referrer_target_url; } if (!base::strcasecmp(GetArgValue("name", argc, argn, argv), "geturlredirectnotify")) { handle_url_redirects_ = true; } NPError error = PluginTest::New(mode, argc, argn, argv, saved); // The above sets test_name(). if (test_name() == "cookies") check_cookies_ = true; return error; } NPError PluginGetURLTest::SetWindow(NPWindow* pNPWindow) { #if !defined(OS_MACOSX) if (pNPWindow->window == NULL) return NPERR_NO_ERROR; #endif if (!tests_started_) { tests_started_ = true; tests_in_progress_++; if (expect_404_response_) { HostFunctions()->geturl(id(), page_not_found_url_.c_str(), NULL); return NPERR_NO_ERROR; } else if (!fail_write_url_.empty()) { HostFunctions()->geturl(id(), fail_write_url_.c_str(), NULL); return NPERR_NO_ERROR; } else if (!referrer_target_url_.empty()) { HostFunctions()->pushpopupsenabledstate(id(), true); HostFunctions()->geturl(id(), referrer_target_url_.c_str(), "_blank"); HostFunctions()->poppopupsenabledstate(id()); return NPERR_NO_ERROR; } else if (handle_url_redirects_) { HostFunctions()->geturlnotify( id(), REDIRECT_SRC_URL, NULL, reinterpret_cast<void*>(REDIRECT_SRC_URL_NOTIFICATION_CANCEL_ID)); return NPERR_NO_ERROR; } else if (check_cookies_) { HostFunctions()->geturlnotify( id(), "plugin_ref_target_page.html", NULL, reinterpret_cast<void*>(SELF_URL_STREAM_ID)); return NPERR_NO_ERROR; } std::string url = SELF_URL; HostFunctions()->geturlnotify(id(), url.c_str(), NULL, reinterpret_cast<void*>(SELF_URL_STREAM_ID)); tests_in_progress_++; std::string bogus_url = BOGUS_URL; HostFunctions()->geturlnotify(id(), bogus_url.c_str(), NULL, reinterpret_cast<void*>(BOGUS_URL_STREAM_ID)); } return NPERR_NO_ERROR; } NPError PluginGetURLTest::NewStream(NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype) { if (stream == NULL) { SetError("NewStream got null stream"); return NPERR_INVALID_PARAM; } if (test_completed()) { return PluginTest::NewStream(type, stream, seekable, stype); } if (!referrer_target_url_.empty()) { return NPERR_NO_ERROR; } COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData), cast_validity_check); if (expect_404_response_) { NPObject *window_obj = NULL; HostFunctions()->getvalue(id(), NPNVWindowNPObject, &window_obj); if (!window_obj) { SetError("Failed to get NPObject for plugin instance2"); SignalTestCompleted(); return NPERR_NO_ERROR; } std::string script = "javascript:document.title=\"OK\""; NPString script_string; script_string.UTF8Characters = script.c_str(); script_string.UTF8Length = static_cast<unsigned int>(script.length()); NPVariant result_var; npn_evaluate_context_ = true; HostFunctions()->evaluate(id(), window_obj, &script_string, &result_var); npn_evaluate_context_ = false; return NPERR_NO_ERROR; } if (!fail_write_url_.empty() || check_cookies_) { return NPERR_NO_ERROR; } unsigned long stream_id = reinterpret_cast<unsigned long>( stream->notifyData); switch (stream_id) { case SELF_URL_STREAM_ID: break; case FETCHED_URL_STREAM_ID: { std::string filename = self_url_; if (filename.find("file:///", 0) != 0) { SetError("Test expects a file-url."); break; } // TODO(evanm): use the net:: functions to convert file:// URLs to // on-disk file paths. But it probably doesn't actually matter in // this test. #if defined(OS_WIN) filename = filename.substr(8); // remove "file:///" // Assume an ASCII path on Windows. base::FilePath path = base::FilePath(ASCIIToWide(filename)); #else filename = filename.substr(7); // remove "file://" base::FilePath path = base::FilePath(filename); #endif test_file_ = base::OpenFile(path, "r"); if (!test_file_) { SetError("Could not open source file"); } } break; case BOGUS_URL_STREAM_ID: SetError("Unexpected NewStream for BOGUS_URL"); break; case REDIRECT_SRC_URL_NOTIFICATION_CANCEL_ID: SetError("Should not redirect to URL when plugin denied it."); break; case REDIRECT_SRC_URL_NOTIFICATION_ALLOW_ID: break; default: SetError("Unexpected NewStream callback"); break; } return NPERR_NO_ERROR; } int32 PluginGetURLTest::WriteReady(NPStream *stream) { if (test_completed()) { return PluginTest::WriteReady(stream); } if (!referrer_target_url_.empty() || check_cookies_) { return STREAM_CHUNK; } COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData), cast_validity_check); unsigned long stream_id = reinterpret_cast<unsigned long>( stream->notifyData); if (stream_id == BOGUS_URL_STREAM_ID) SetError("Received WriteReady for BOGUS_URL"); return STREAM_CHUNK; } int32 PluginGetURLTest::Write(NPStream *stream, int32 offset, int32 len, void *buffer) { if (test_completed()) { return PluginTest::Write(stream, offset, len, buffer); } if (!fail_write_url_.empty()) { SignalTestCompleted(); return -1; } if (!referrer_target_url_.empty() || check_cookies_) { return len; } if (stream == NULL) { SetError("Write got null stream"); return -1; } if (len < 0 || len > STREAM_CHUNK) { SetError("Write got bogus stream chunk size"); return -1; } COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData), cast_validity_check); unsigned long stream_id = reinterpret_cast<unsigned long>( stream->notifyData); switch (stream_id) { case SELF_URL_STREAM_ID: self_url_.append(static_cast<char*>(buffer), len); break; case FETCHED_URL_STREAM_ID: { char read_buffer[STREAM_CHUNK]; int32 bytes = static_cast<int32>(fread(read_buffer, 1, len, test_file_)); // Technically, fread could return fewer than len // bytes. But this is not likely. if (bytes != len) SetError("Did not read correct bytelength from source file"); if (memcmp(read_buffer, buffer, len)) SetError("Content mismatch between data and source!"); } break; case BOGUS_URL_STREAM_ID: SetError("Unexpected write callback for BOGUS_URL"); break; case REDIRECT_SRC_URL_NOTIFICATION_ALLOW_ID: break; default: SetError("Unexpected write callback"); break; } // Pretend that we took all the data. return len; } NPError PluginGetURLTest::DestroyStream(NPStream *stream, NPError reason) { if (test_completed()) { return PluginTest::DestroyStream(stream, reason); } if (stream == NULL) { SetError("NewStream got null stream"); return NPERR_INVALID_PARAM; } COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData), cast_validity_check); if (expect_404_response_) { if (npn_evaluate_context_) { SetError("Received destroyStream in the context of NPN_Evaluate."); } SignalTestCompleted(); return NPERR_NO_ERROR; } if (!referrer_target_url_.empty()) { return NPERR_NO_ERROR; } if (check_cookies_) { SignalTestCompleted(); return NPERR_NO_ERROR; } unsigned long stream_id = reinterpret_cast<unsigned long>(stream->notifyData); switch (stream_id) { case SELF_URL_STREAM_ID: // don't care break; case REDIRECT_SRC_URL_NOTIFICATION_ALLOW_ID: break; case FETCHED_URL_STREAM_ID: { char read_buffer[STREAM_CHUNK]; size_t bytes = fread(read_buffer, 1, sizeof(read_buffer), test_file_); if (bytes != 0) SetError("Data and source mismatch on length"); base::CloseFile(test_file_); } break; default: SetError("Unexpected NewStream callback"); break; } return NPERR_NO_ERROR; } void PluginGetURLTest::StreamAsFile(NPStream* stream, const char* fname) { if (stream == NULL) { SetError("NewStream got null stream"); return; } COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData), cast_validity_check); unsigned long stream_id = reinterpret_cast<unsigned long>(stream->notifyData); switch (stream_id) { case SELF_URL_STREAM_ID: // don't care break; default: SetError("Unexpected NewStream callback"); break; } } void PluginGetURLTest::URLNotify(const char* url, NPReason reason, void* data) { if (!tests_in_progress_) { SetError("URLNotify received after tests completed"); return; } if (!url) { SetError("URLNotify received NULL url"); return; } if (check_cookies_) return; COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(data), cast_validity_check); unsigned long stream_id = reinterpret_cast<unsigned long>(data); switch (stream_id) { case SELF_URL_STREAM_ID: if (strcmp(url, SELF_URL) != 0) SetError("URLNotify reported incorrect url for SELF_URL"); // We have our stream url. Go fetch it. HostFunctions()->geturlnotify(id(), self_url_.c_str(), NULL, reinterpret_cast<void*>(FETCHED_URL_STREAM_ID)); break; case FETCHED_URL_STREAM_ID: if (!url || strcmp(url, self_url_.c_str()) != 0) SetError("URLNotify reported incorrect url for FETCHED_URL"); tests_in_progress_--; break; case BOGUS_URL_STREAM_ID: if (reason != NPRES_NETWORK_ERR) { std::string err = "BOGUS_URL received unexpected URLNotify status: "; err.append(base::IntToString(reason)); SetError(err); } tests_in_progress_--; break; case REDIRECT_SRC_URL_NOTIFICATION_CANCEL_ID: { if (!received_url_redirect_cancel_notification_) { SetError("Failed to receive URLRedirect notification for cancel"); } if (reason != NPRES_NETWORK_ERR) { SetError("Redirected URL didn't get canceled"); } break; } case REDIRECT_SRC_URL_NOTIFICATION_ALLOW_ID: { if (!received_url_redirect_allow_notification_) { SetError("Failed to receive URLRedirect notification for allow"); } if (reason != NPRES_DONE) { SetError("Redirected URL didn't complete successfully"); } tests_in_progress_--; break; } default: SetError("Unexpected NewStream callback"); break; } if (tests_in_progress_ == 0) SignalTestCompleted(); } void PluginGetURLTest::URLRedirectNotify(const char* url, int32_t status, void* notify_data) { unsigned long stream_id = reinterpret_cast<unsigned long>(notify_data); if (stream_id == REDIRECT_SRC_URL_NOTIFICATION_CANCEL_ID) { if (!base::strcasecmp(url, "http://mock.http/npapi/plugin_read_page.html")) { received_url_redirect_cancel_notification_ = true; // Disallow redirect notification. HostFunctions()->urlredirectresponse(id(), notify_data, false); // Now start a request that we will allow to redirect. HostFunctions()->geturlnotify( id(), REDIRECT_SRC_URL, NULL, reinterpret_cast<void*>(REDIRECT_SRC_URL_NOTIFICATION_ALLOW_ID)); } } else if (stream_id == REDIRECT_SRC_URL_NOTIFICATION_ALLOW_ID) { received_url_redirect_allow_notification_ = true; HostFunctions()->urlredirectresponse(id(), notify_data, true); } } } // namespace NPAPIClient