普通文本  |  478行  |  14.25 KB

// 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(base::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