chromium/ppapi/tests/test_file_chooser.cc

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ppapi/tests/test_file_chooser.h"

#include <stddef.h>

#include "ppapi/c/pp_errors.h"
#include "ppapi/c/ppb_file_io.h"
#include "ppapi/cpp/dev/file_chooser_dev.h"
#include "ppapi/cpp/file_io.h"
#include "ppapi/cpp/trusted/file_chooser_trusted.h"
#include "ppapi/cpp/var.h"

namespace {

// Test data written by WriteDefaultContentsToFile().
const char kTestFileContents[] = "Hello from PPAPI";
const size_t kTestFileContentsSizeBytes = sizeof(kTestFileContents) - 1;

}  // namespace

REGISTER_TEST_CASE(FileChooser);

bool TestFileChooser::Init() {
  return CheckTestingInterface() && EnsureRunningOverHTTP();
}

void TestFileChooser::RunTests(const std::string& filter) {
  RUN_TEST(OpenSimple, filter);
  RUN_TEST(OpenCancel, filter);
  RUN_TEST(SaveAsSafeDefaultName, filter);
  RUN_TEST(SaveAsUnsafeDefaultName, filter);
  RUN_TEST(SaveAsCancel, filter);
  RUN_TEST(SaveAsDangerousExecutableAllowed, filter);
  RUN_TEST(SaveAsDangerousExecutableDisallowed, filter);
  RUN_TEST(SaveAsDangerousExtensionListDisallowed, filter);
}

bool TestFileChooser::WriteDefaultContentsToFile(const pp::FileRef& file_ref) {
  TestCompletionCallback fileio_callback(instance_->pp_instance(),
                                         callback_type());
  pp::FileIO fileio(instance());

  fileio_callback.WaitForResult(
      fileio.Open(file_ref, PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_CREATE,
                  fileio_callback.GetCallback()));
  if (fileio_callback.result() != PP_OK)
    return false;

  fileio_callback.WaitForResult(fileio.Write(0, kTestFileContents,
                                             kTestFileContentsSizeBytes,
                                             fileio_callback.GetCallback()));
  return fileio_callback.result() == kTestFileContentsSizeBytes;
}

// Tests that the plugin can invoke a simple file chooser and that the returned
// file can be read from. Note that this test doesn't test that the accepted
// file type list is honored.
std::string TestFileChooser::TestOpenSimple() {
  pp::FileChooser_Dev file_chooser(instance(), PP_FILECHOOSERMODE_OPEN, "*");
  ASSERT_FALSE(file_chooser.is_null());

  TestCompletionCallbackWithOutput<std::vector<pp::FileRef>>
      filechooser_callback(instance_->pp_instance(), callback_type());
  filechooser_callback.WaitForResult(
      file_chooser.Show(filechooser_callback.GetCallback()));

  const std::vector<pp::FileRef>& output_ref = filechooser_callback.output();
  ASSERT_EQ(1u, output_ref.size());

  TestCompletionCallback fileio_callback(instance_->pp_instance(),
                                         callback_type());
  pp::FileIO fileio(instance());
  fileio_callback.WaitForResult(fileio.Open(
      output_ref.front(), PP_FILEOPENFLAG_READ, fileio_callback.GetCallback()));
  ASSERT_EQ(PP_OK, fileio_callback.result());
  PASS();
}

// Tests the behavior when the user cancels the file chooser. Browser-side logic
// for simulating the cancellation can be found at
// ppapi_filechooser_browsertest.cc
std::string TestFileChooser::TestOpenCancel() {
  pp::FileChooser_Dev file_chooser(instance(), PP_FILECHOOSERMODE_OPEN, "*");
  ASSERT_FALSE(file_chooser.is_null());

  TestCompletionCallbackWithOutput<std::vector<pp::FileRef>>
      filechooser_callback(instance_->pp_instance(), callback_type());
  filechooser_callback.WaitForResult(
      file_chooser.Show(filechooser_callback.GetCallback()));

  const std::vector<pp::FileRef>& output_ref = filechooser_callback.output();
  ASSERT_EQ(0u, output_ref.size());
  PASS();
}

// Tests that the plugin can invoke a "Save as" dialog using the
// FileChooser_Trusted API and that the returned FileRef can be written to.
std::string TestFileChooser::TestSaveAsSafeDefaultName() {
  pp::FileChooser_Trusted file_chooser(instance(), PP_FILECHOOSERMODE_OPEN,
                                       ".txt", true /* save_as */,
                                       "innocuous.txt");
  ASSERT_FALSE(file_chooser.is_null());

  TestCompletionCallbackWithOutput<std::vector<pp::FileRef>>
      filechooser_callback(instance_->pp_instance(), callback_type());
  filechooser_callback.WaitForResult(
      file_chooser.Show(filechooser_callback.GetCallback()));

  const std::vector<pp::FileRef>& output_ref = filechooser_callback.output();
  ASSERT_EQ(1u, output_ref.size());

  ASSERT_TRUE(WriteDefaultContentsToFile(output_ref.front()));
  PASS();
}

// Similar to the previous test, but tests that an unsafe filename passed as the
// suggested name is sanitized.
std::string TestFileChooser::TestSaveAsUnsafeDefaultName() {
  pp::FileChooser_Trusted file_chooser(instance(), PP_FILECHOOSERMODE_OPEN,
                                       "text/plain,.html", true /* save_as */,
                                       "unsafe.txt ");
  ASSERT_FALSE(file_chooser.is_null());

  TestCompletionCallbackWithOutput<std::vector<pp::FileRef>>
      filechooser_callback(instance_->pp_instance(), callback_type());
  filechooser_callback.WaitForResult(
      file_chooser.Show(filechooser_callback.GetCallback()));

  const std::vector<pp::FileRef>& output_ref = filechooser_callback.output();
  ASSERT_EQ(1u, output_ref.size());
  ASSERT_EQ("unsafe.txt_", output_ref.front().GetName().AsString());

  ASSERT_TRUE(WriteDefaultContentsToFile(output_ref.front()));
  PASS();
}

// Tests the behavior when the user cancels a Save As file chooser. Requires
// that the test runner cancel the Save As dialog.
std::string TestFileChooser::TestSaveAsCancel() {
  pp::FileChooser_Trusted file_chooser(instance(), PP_FILECHOOSERMODE_OPEN, "*",
                                       true /* save as */, "anything.txt");
  ASSERT_FALSE(file_chooser.is_null());

  TestCompletionCallbackWithOutput<std::vector<pp::FileRef>>
      filechooser_callback(instance_->pp_instance(), callback_type());
  filechooser_callback.WaitForResult(
      file_chooser.Show(filechooser_callback.GetCallback()));

  const std::vector<pp::FileRef>& output_ref = filechooser_callback.output();
  ASSERT_EQ(0u, output_ref.size());
  PASS();
}

// Checks that a dangerous file is allowed to be downloaded via the
// FileChooser_Trusted API. Chrome should delegate the decision of which files
// are allowed over to SafeBrowsing (if enabled), and the current SafeBrowsing
// configuration should allow downloading of dangerous files for this test to
// work.
std::string TestFileChooser::TestSaveAsDangerousExecutableAllowed() {
  pp::FileChooser_Trusted file_chooser(instance(), PP_FILECHOOSERMODE_OPEN,
                                       ".exe", true /* save_as */,
                                       "dangerous.exe");
  ASSERT_FALSE(file_chooser.is_null());

  TestCompletionCallbackWithOutput<std::vector<pp::FileRef>>
      filechooser_callback(instance_->pp_instance(), callback_type());
  filechooser_callback.WaitForResult(
      file_chooser.Show(filechooser_callback.GetCallback()));

  const std::vector<pp::FileRef>& output_ref = filechooser_callback.output();
  ASSERT_EQ(1u, output_ref.size());
  ASSERT_EQ("dangerous.exe", output_ref.front().GetName().AsString());

  ASSERT_TRUE(WriteDefaultContentsToFile(output_ref.front()));
  PASS();
}

// Checks that a dangerous file is not allowed to be downloaded via the
// FileChooser_Trusted API. Chrome should delegate the decision of which files
// are allowed over to SafeBrowsing (if enabled), and the current SafeBrowsing
// configuration should disallow downloading of dangerous files for this test to
// work.
std::string TestFileChooser::TestSaveAsDangerousExecutableDisallowed() {
  pp::FileChooser_Trusted file_chooser(instance(), PP_FILECHOOSERMODE_OPEN,
                                       ".exe", true /* save_as */,
                                       "dangerous.exe");
  ASSERT_FALSE(file_chooser.is_null());

  TestCompletionCallbackWithOutput<std::vector<pp::FileRef>>
      filechooser_callback(instance_->pp_instance(), callback_type());
  filechooser_callback.WaitForResult(
      file_chooser.Show(filechooser_callback.GetCallback()));

  const std::vector<pp::FileRef>& output_ref = filechooser_callback.output();
  ASSERT_EQ(0u, output_ref.size());
  PASS();
}

// Checks that a dangerous file is not allowed to be downloaded via the
// FileChooser_Trusted API. Chrome should delegate the decision of which files
// are allowed over to SafeBrowsing (if enabled), and the current SafeBrowsing
// configuration should disallow downloading of dangerous files for this test to
// work.
std::string TestFileChooser::TestSaveAsDangerousExtensionListDisallowed() {
  pp::FileChooser_Trusted file_chooser(instance(), PP_FILECHOOSERMODE_OPEN,
                                       ".txt,.exe", true /* save_as */,
                                       "innocuous.txt");
  ASSERT_FALSE(file_chooser.is_null());

  TestCompletionCallbackWithOutput<std::vector<pp::FileRef>>
      filechooser_callback(instance_->pp_instance(), callback_type());
  filechooser_callback.WaitForResult(
      file_chooser.Show(filechooser_callback.GetCallback()));

  const std::vector<pp::FileRef>& output_ref = filechooser_callback.output();
  ASSERT_EQ(0u, output_ref.size());
  PASS();
}