blob: b9c43dbc0e236d408b2e7fbb388d1d8182d766b9 [file] [log] [blame]
// Copyright 2019 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 "chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.h"
#include <memory>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/files/file_path.h"
#include "base/json/json_string_value_serializer.h"
#include "base/values.h"
#include "chrome/browser/chromeos/printing/printing_stubs.h"
#include "chrome/browser/download/chrome_download_manager_delegate.h"
#include "chrome/browser/download/download_core_service_factory.h"
#include "chrome/browser/download/download_core_service_impl.h"
#include "chrome/browser/ui/chrome_select_file_policy.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/debug_daemon/debug_daemon_client.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_web_ui.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/shell_dialogs/select_file_dialog.h"
#include "ui/shell_dialogs/select_file_dialog_factory.h"
namespace chromeos {
namespace settings {
class CupsPrintersHandlerTest;
namespace {
// Converts JSON string to base::ListValue object.
// On failure, returns NULL and fills |*error| string.
std::unique_ptr<base::ListValue> GetJSONAsListValue(const std::string& json,
std::string* error) {
auto ret = base::ListValue::From(
JSONStringValueDeserializer(json).Deserialize(nullptr, error));
if (!ret)
*error = "Value is not a list.";
return ret;
}
} // namespace
// Callback used for testing CupsAddAutoConfiguredPrinter().
void AddedPrinter(int32_t status) {
ASSERT_EQ(status, 0);
}
// Callback used for testing CupsRemovePrinter().
void RemovedPrinter(base::OnceClosure quit_closure,
bool* expected,
bool result) {
*expected = result;
std::move(quit_closure).Run();
}
class TestCupsPrintersManager : public StubCupsPrintersManager {
public:
base::Optional<Printer> GetPrinter(const std::string& id) const override {
return Printer();
}
};
class FakePpdProvider : public PpdProvider {
public:
FakePpdProvider() = default;
void ResolveManufacturers(ResolveManufacturersCallback cb) override {}
void ResolvePrinters(const std::string& manufacturer,
ResolvePrintersCallback cb) override {}
void ResolvePpdReference(const PrinterSearchData& search_data,
ResolvePpdReferenceCallback cb) override {}
void ResolvePpd(const Printer::PpdReference& reference,
ResolvePpdCallback cb) override {}
void ResolvePpdLicense(base::StringPiece effective_make_and_model,
ResolvePpdLicenseCallback cb) override {}
void ReverseLookup(const std::string& effective_make_and_model,
ReverseLookupCallback cb) override {}
private:
~FakePpdProvider() override {}
};
class TestSelectFilePolicy : public ui::SelectFilePolicy {
public:
TestSelectFilePolicy& operator=(const TestSelectFilePolicy&) = delete;
bool CanOpenSelectFileDialog() override { return true; }
void SelectFileDenied() override {}
};
// A fake ui::SelectFileDialog, which will cancel the file selection instead of
// selecting a file and verify that the extensions are correctly set.
class FakeSelectFileDialog : public ui::SelectFileDialog {
public:
FakeSelectFileDialog(Listener* listener,
std::unique_ptr<ui::SelectFilePolicy> policy,
FileTypeInfo* file_type)
: ui::SelectFileDialog(listener, std::move(policy)),
expected_file_type_info_(file_type) {}
FakeSelectFileDialog(const FakeSelectFileDialog&) = delete;
FakeSelectFileDialog& operator=(const FakeSelectFileDialog&) = delete;
protected:
void SelectFileImpl(Type type,
const base::string16& title,
const base::FilePath& default_path,
const FileTypeInfo* file_types,
int file_type_index,
const base::FilePath::StringType& default_extension,
gfx::NativeWindow owning_window,
void* params) override {
// Check that the extensions we expect match the actual extensions passed
// from the CupsPrintersHandler.
VerifyExtensions(file_types);
// Close the file select dialog.
listener_->FileSelectionCanceled(params);
}
bool IsRunning(gfx::NativeWindow owning_window) const override {
return true;
}
void ListenerDestroyed() override {}
bool HasMultipleFileTypeChoicesImpl() override { return false; }
void VerifyExtensions(const FileTypeInfo* file_types) {
const std::vector<std::vector<base::FilePath::StringType>>& actual_exts =
file_types->extensions;
std::vector<std::vector<base::FilePath::StringType>> expected_exts =
expected_file_type_info_->extensions;
for (std::vector<base::FilePath::StringType> actual : actual_exts) {
bool is_equal = false;
std::sort(actual.begin(), actual.end());
for (auto expected_it = expected_exts.begin();
expected_it != expected_exts.end(); ++expected_it) {
std::vector<base::FilePath::StringType>& expected = *expected_it;
std::sort(expected.begin(), expected.end());
if (expected == actual) {
is_equal = true;
expected_exts.erase(expected_it);
break;
}
}
ASSERT_TRUE(is_equal);
}
}
private:
~FakeSelectFileDialog() override = default;
ui::SelectFileDialog::FileTypeInfo* expected_file_type_info_;
};
// A factory associated with the artificial file picker.
class TestSelectFileDialogFactory : public ui::SelectFileDialogFactory {
public:
explicit TestSelectFileDialogFactory(
ui::SelectFileDialog::FileTypeInfo* expected_file_type_info)
: expected_file_type_info_(expected_file_type_info) {}
ui::SelectFileDialog* Create(
ui::SelectFileDialog::Listener* listener,
std::unique_ptr<ui::SelectFilePolicy> policy) override {
// TODO(jimmyxgong): Investigate why using |policy| created by
// CupsPrintersHandler crashes the test.
return new FakeSelectFileDialog(listener,
std::make_unique<TestSelectFilePolicy>(),
expected_file_type_info_);
}
TestSelectFileDialogFactory(const TestSelectFileDialogFactory&) = delete;
TestSelectFileDialogFactory& operator=(const TestSelectFileDialogFactory&) =
delete;
private:
ui::SelectFileDialog::FileTypeInfo* expected_file_type_info_;
};
class CupsPrintersHandlerTest : public testing::Test {
public:
CupsPrintersHandlerTest()
: task_environment_(content::BrowserTaskEnvironment::REAL_IO_THREAD),
profile_(),
web_ui_(),
printers_handler_() {}
~CupsPrintersHandlerTest() override = default;
void SetUp() override {
printers_handler_ = CupsPrintersHandler::CreateForTesting(
&profile_, base::MakeRefCounted<FakePpdProvider>(),
std::make_unique<StubPrinterConfigurer>(), &printers_manager_);
printers_handler_->SetWebUIForTest(&web_ui_);
printers_handler_->RegisterMessages();
}
protected:
// Must outlive |profile_|.
content::BrowserTaskEnvironment task_environment_;
TestingProfile profile_;
content::TestWebUI web_ui_;
std::unique_ptr<CupsPrintersHandler> printers_handler_;
TestCupsPrintersManager printers_manager_;
};
TEST_F(CupsPrintersHandlerTest, RemoveCorrectPrinter) {
DBusThreadManager::Initialize();
DebugDaemonClient* client = DBusThreadManager::Get()->GetDebugDaemonClient();
client->CupsAddAutoConfiguredPrinter("testprinter1", "fakeuri",
base::BindOnce(&AddedPrinter));
const std::string remove_list = R"(
["testprinter1", "Test Printer 1"]
)";
std::string error;
std::unique_ptr<base::ListValue> remove_printers(
GetJSONAsListValue(remove_list, &error));
ASSERT_TRUE(remove_printers) << "Error deserializing list: " << error;
web_ui_.HandleReceivedMessage("removeCupsPrinter", remove_printers.get());
// We expect this printer removal to fail since the printer should have
// already been removed by the previous call to 'removeCupsPrinter'.
base::RunLoop run_loop;
bool expected = true;
client->CupsRemovePrinter(
"testprinter1",
base::BindRepeating(&RemovedPrinter, run_loop.QuitClosure(), &expected),
base::DoNothing());
run_loop.Run();
EXPECT_FALSE(expected);
}
TEST_F(CupsPrintersHandlerTest, VerifyOnlyPpdFilesAllowed) {
DownloadCoreServiceFactory::GetForBrowserContext(&profile_)
->SetDownloadManagerDelegateForTesting(
std::make_unique<ChromeDownloadManagerDelegate>(&profile_));
ui::SelectFileDialog::FileTypeInfo expected_file_type_info;
// We only allow .ppd and .ppd.gz file extensions for our file select dialog.
expected_file_type_info.extensions.push_back({"ppd"});
expected_file_type_info.extensions.push_back({"ppd.gz"});
ui::SelectFileDialog::SetFactory(
new TestSelectFileDialogFactory(&expected_file_type_info));
base::Value args(base::Value::Type::LIST);
args.Append("handleFunctionName");
web_ui_.HandleReceivedMessage("selectPPDFile",
&base::Value::AsListValue(args));
}
} // namespace settings.
} // namespace chromeos.