15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2011 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "content/test/plugin/plugin_geturl_test.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdio.h>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/basictypes.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/file_util.h"
11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
127d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/strings/string_util.h"
13868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// url for "self".  The %22%22 is to make a statement for javascript to
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// evaluate and return.
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define SELF_URL "javascript:window.location+\"\""
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The identifier for the self url stream.
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define SELF_URL_STREAM_ID 1
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The identifier for the fetched url stream.
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define FETCHED_URL_STREAM_ID 2
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// url for testing GetURL with a bogus URL.
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define BOGUS_URL "bogoproto:///x:/asdf.xysdhffieasdf.asdhj/"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// url for testing redirect notifications sent to plugins.
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define REDIRECT_SRC_URL \
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "http://mock.http/npapi/plugin_read_page_redirect_src.html"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The notification id for the redirect notification url.
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define REDIRECT_SRC_URL_NOTIFICATION_ID 4
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The identifier for the bogus url stream.
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define BOGUS_URL_STREAM_ID 3
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The maximum chunk size of stream data.
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define STREAM_CHUNK 197
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace NPAPIClient {
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PluginGetURLTest::PluginGetURLTest(NPP id, NPNetscapeFuncs *host_functions)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  : PluginTest(id, host_functions),
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tests_started_(false),
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tests_in_progress_(0),
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    test_file_(NULL),
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    expect_404_response_(false),
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    npn_evaluate_context_(false),
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handle_url_redirects_(false),
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    received_url_redirect_notification_(false) {
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PluginGetURLTest::~PluginGetURLTest() {}
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NPError PluginGetURLTest::New(uint16 mode, int16 argc, const char* argn[],
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              const char* argv[], NPSavedData* saved) {
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char* page_not_found_url = GetArgValue("page_not_found_url", argc,
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                               argn, argv);
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (page_not_found_url) {
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    page_not_found_url_ = page_not_found_url;
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    expect_404_response_ = true;
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char* fail_write_url = GetArgValue("fail_write_url", argc,
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                           argn, argv);
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (fail_write_url) {
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fail_write_url_ = fail_write_url;
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const char* referrer_target_url = GetArgValue("ref_target", argc,
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                argn, argv);
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (referrer_target_url) {
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    referrer_target_url_ = referrer_target_url;
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!base::strcasecmp(GetArgValue("name", argc, argn, argv),
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        "geturlredirectnotify")) {
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    handle_url_redirects_ = true;
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return PluginTest::New(mode, argc, argn, argv, saved);
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NPError PluginGetURLTest::SetWindow(NPWindow* pNPWindow) {
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if !defined(OS_MACOSX)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (pNPWindow->window == NULL)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NPERR_NO_ERROR;
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!tests_started_) {
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tests_started_ = true;
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tests_in_progress_++;
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (expect_404_response_) {
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      HostFunctions()->geturl(id(), page_not_found_url_.c_str(), NULL);
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return NPERR_NO_ERROR;
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (!fail_write_url_.empty()) {
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      HostFunctions()->geturl(id(), fail_write_url_.c_str(), NULL);
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return NPERR_NO_ERROR;
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (!referrer_target_url_.empty()) {
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      HostFunctions()->pushpopupsenabledstate(id(), true);
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      HostFunctions()->geturl(id(), referrer_target_url_.c_str(), "_blank");
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      HostFunctions()->poppopupsenabledstate(id());
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return NPERR_NO_ERROR;
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (handle_url_redirects_) {
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      HostFunctions()->geturlnotify(
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          id(), REDIRECT_SRC_URL, NULL,
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          reinterpret_cast<void*>(REDIRECT_SRC_URL_NOTIFICATION_ID));
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return NPERR_NO_ERROR;
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string url = SELF_URL;
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    HostFunctions()->geturlnotify(id(), url.c_str(), NULL,
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  reinterpret_cast<void*>(SELF_URL_STREAM_ID));
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tests_in_progress_++;
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string bogus_url = BOGUS_URL;
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    HostFunctions()->geturlnotify(id(), bogus_url.c_str(), NULL,
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  reinterpret_cast<void*>(BOGUS_URL_STREAM_ID));
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return NPERR_NO_ERROR;
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NPError PluginGetURLTest::NewStream(NPMIMEType type, NPStream* stream,
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              NPBool seekable, uint16* stype) {
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (stream == NULL) {
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetError("NewStream got null stream");
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NPERR_INVALID_PARAM;
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (test_completed()) {
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return PluginTest::NewStream(type, stream, seekable, stype);
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!referrer_target_url_.empty()) {
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NPERR_NO_ERROR;
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 cast_validity_check);
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (expect_404_response_) {
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NPObject *window_obj = NULL;
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    HostFunctions()->getvalue(id(), NPNVWindowNPObject, &window_obj);
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!window_obj) {
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetError("Failed to get NPObject for plugin instance2");
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SignalTestCompleted();
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return NPERR_NO_ERROR;
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string script = "javascript:document.title=\"OK\"";
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NPString script_string;
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    script_string.UTF8Characters = script.c_str();
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    script_string.UTF8Length = static_cast<unsigned int>(script.length());
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NPVariant result_var;
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    npn_evaluate_context_ = true;
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    HostFunctions()->evaluate(id(), window_obj, &script_string, &result_var);
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    npn_evaluate_context_ = false;
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NPERR_NO_ERROR;
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!fail_write_url_.empty()) {
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NPERR_NO_ERROR;
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unsigned long stream_id = reinterpret_cast<unsigned long>(
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      stream->notifyData);
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (stream_id) {
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case SELF_URL_STREAM_ID:
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case FETCHED_URL_STREAM_ID:
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      {
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        std::string filename = self_url_;
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (filename.find("file:///", 0) != 0) {
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          SetError("Test expects a file-url.");
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          break;
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // TODO(evanm): use the net:: functions to convert file:// URLs to
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // on-disk file paths.  But it probably doesn't actually matter in
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // this test.
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN)
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        filename = filename.substr(8);  // remove "file:///"
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Assume an ASCII path on Windows.
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::FilePath path = base::FilePath(ASCIIToWide(filename));
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        filename = filename.substr(7);  // remove "file://"
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::FilePath path = base::FilePath(filename);
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        test_file_ = file_util::OpenFile(path, "r");
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!test_file_) {
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          SetError("Could not open source file");
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case BOGUS_URL_STREAM_ID:
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetError("Unexpected NewStream for BOGUS_URL");
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case REDIRECT_SRC_URL_NOTIFICATION_ID:
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetError("Should not redirect to URL when plugin denied it.");
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetError("Unexpected NewStream callback");
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return NPERR_NO_ERROR;
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int32 PluginGetURLTest::WriteReady(NPStream *stream) {
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (test_completed()) {
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return PluginTest::WriteReady(stream);
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!referrer_target_url_.empty()) {
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return STREAM_CHUNK;
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 cast_validity_check);
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unsigned long stream_id = reinterpret_cast<unsigned long>(
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      stream->notifyData);
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (stream_id == BOGUS_URL_STREAM_ID)
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetError("Received WriteReady for BOGUS_URL");
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return STREAM_CHUNK;
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int32 PluginGetURLTest::Write(NPStream *stream, int32 offset, int32 len,
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              void *buffer) {
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (test_completed()) {
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return PluginTest::Write(stream, offset, len, buffer);
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!fail_write_url_.empty()) {
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SignalTestCompleted();
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return -1;
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!referrer_target_url_.empty()) {
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return len;
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (stream == NULL) {
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetError("Write got null stream");
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return -1;
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (len < 0 || len > STREAM_CHUNK) {
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetError("Write got bogus stream chunk size");
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return -1;
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 cast_validity_check);
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unsigned long stream_id = reinterpret_cast<unsigned long>(
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      stream->notifyData);
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (stream_id) {
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case SELF_URL_STREAM_ID:
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self_url_.append(static_cast<char*>(buffer), len);
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case FETCHED_URL_STREAM_ID:
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      {
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        char read_buffer[STREAM_CHUNK];
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        int32 bytes =
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            static_cast<int32>(fread(read_buffer, 1, len, test_file_));
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Technically, fread could return fewer than len
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // bytes.  But this is not likely.
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (bytes != len)
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          SetError("Did not read correct bytelength from source file");
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (memcmp(read_buffer, buffer, len))
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          SetError("Content mismatch between data and source!");
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case BOGUS_URL_STREAM_ID:
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetError("Unexpected write callback for BOGUS_URL");
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetError("Unexpected write callback");
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Pretend that we took all the data.
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return len;
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)NPError PluginGetURLTest::DestroyStream(NPStream *stream, NPError reason) {
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (test_completed()) {
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return PluginTest::DestroyStream(stream, reason);
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (stream == NULL) {
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetError("NewStream got null stream");
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NPERR_INVALID_PARAM;
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 cast_validity_check);
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (expect_404_response_) {
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (npn_evaluate_context_) {
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetError("Received destroyStream in the context of NPN_Evaluate.");
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SignalTestCompleted();
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NPERR_NO_ERROR;
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!referrer_target_url_.empty()) {
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NPERR_NO_ERROR;
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unsigned long stream_id =
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      reinterpret_cast<unsigned long>(stream->notifyData);
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (stream_id) {
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case SELF_URL_STREAM_ID:
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // don't care
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case FETCHED_URL_STREAM_ID:
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      {
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        char read_buffer[STREAM_CHUNK];
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        size_t bytes = fread(read_buffer, 1, sizeof(read_buffer), test_file_);
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (bytes != 0)
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          SetError("Data and source mismatch on length");
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        file_util::CloseFile(test_file_);
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetError("Unexpected NewStream callback");
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return NPERR_NO_ERROR;
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PluginGetURLTest::StreamAsFile(NPStream* stream, const char* fname) {
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (stream == NULL) {
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetError("NewStream got null stream");
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(stream->notifyData),
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 cast_validity_check);
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unsigned long stream_id =
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      reinterpret_cast<unsigned long>(stream->notifyData);
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (stream_id) {
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case SELF_URL_STREAM_ID:
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // don't care
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetError("Unexpected NewStream callback");
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PluginGetURLTest::URLNotify(const char* url, NPReason reason, void* data) {
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!tests_in_progress_) {
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetError("URLNotify received after tests completed");
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!url) {
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetError("URLNotify received NULL url");
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  COMPILE_ASSERT(sizeof(unsigned long) <= sizeof(data), cast_validity_check);
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unsigned long stream_id = reinterpret_cast<unsigned long>(data);
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (stream_id) {
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case SELF_URL_STREAM_ID:
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (strcmp(url, SELF_URL) != 0)
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SetError("URLNotify reported incorrect url for SELF_URL");
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We have our stream url.  Go fetch it.
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      HostFunctions()->geturlnotify(id(), self_url_.c_str(), NULL,
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            reinterpret_cast<void*>(FETCHED_URL_STREAM_ID));
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case FETCHED_URL_STREAM_ID:
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!url || strcmp(url, self_url_.c_str()) != 0)
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SetError("URLNotify reported incorrect url for FETCHED_URL");
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tests_in_progress_--;
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case BOGUS_URL_STREAM_ID:
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (reason != NPRES_NETWORK_ERR) {
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        std::string err = "BOGUS_URL received unexpected URLNotify status: ";
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        err.append(base::IntToString(reason));
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SetError(err);
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tests_in_progress_--;
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case REDIRECT_SRC_URL_NOTIFICATION_ID: {
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!received_url_redirect_notification_) {
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SetError("Failed to receive URLRedirect notification");
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tests_in_progress_--;
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetError("Unexpected NewStream callback");
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tests_in_progress_ == 0)
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SignalTestCompleted();
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void PluginGetURLTest::URLRedirectNotify(const char* url,
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                         int32_t status,
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                         void* notify_data) {
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!base::strcasecmp(url, "http://mock.http/npapi/plugin_read_page.html")) {
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    received_url_redirect_notification_ = true;
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Disallow redirect notification.
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    HostFunctions()->urlredirectresponse(id(), notify_data, false);
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace NPAPIClient
421