Commit 65390511 authored by Fabio Rocha's avatar Fabio Rocha Committed by Commit Bot

Reland "desktop-pwas: Introduce shared file/protocol handler registration logic"

This is a reland of https://chromium-review.googlesource.com/c/chromium/src/+/2462383

I needed to update Win7 tests to account for particularities of that OS,
namely the fact that the extension is omitted for launcher filenames
since those are also used as the app's display name.

---
Reason for revert: The tests added here
(WebAppHandlerRegistrationUtilsWinTest.*) are failing on multiple
Win 7 bots, see
https://bugs.chromium.org/p/chromium/issues/detail?id=1139503

Original change's description:
> desktop-pwas: Introduce shared file/protocol handler registration logic
>
> This CL refactors Windows file handler registration in a way that
> exposes shared logic useful for protocol handler registration. Protocol
> handler registration that consumes the shared logic will be added in a
> subsequent CL. This design is detailed in section 4.4 of [1] with a
> summary given below.
>
> The refactor splits up registration by functional concerns,
> with pieces that may be shared with protocol handler registration living
> in web_app_handler_registration_utils_win.h/.cc.
> web_app_file_handler_registration_win.h/.cc is now significantly smaller
> and addresses file-handler-specific concerns in addition to consuming
> the utils API.
>
> File handler registration consists of several pieces:
>
>  1) Create an app registry entry: HKCU\Software\Classes\<app_progid>
>  2) Copy (or hardlink) chrome_pwa_launcher.exe from browser install
>     directory to <profile_dir>\web_applications\<app_id>\<app_name.exe>
>  3) Reregister an app (steps 1 and 2) with a profile-specific name when
>     the same app is installed to a different profile
>     (e.g. "App" -> "App (Profile 1)")
>  4) Create a file type association registry entry:
>     HKCU\Software\Classes\.<file_ext>
>
> In this approach, 1 & 4 are contained in the existing
> ShellUtil::AddFileAssociations function. 2 & 3 are moved to utils
> (CreateAppLauncherFile and CheckAndUpdateExternalInstallations).
>
> Important note about this design:
> - Reregistration logic (3) is altered in this approach. Currently,
> pieces 1, 2, 4 are all executed during reregistration. Because (4) is a
> file-handler only concern, it wouldn't make sense to use existing logic
> from the protocol registration flow. Reregistering (1) and (2) are
> sufficient to rename a duplicate app - this is because file handler
> registry entries (4) only reference the progid of an app which doesn't
> change if its display name is updated (or if the update fails).
> CheckAndUpdateExternalInstallations only updates necessary registration
> pieces to reflect an updated name while leaving existing file/protocol
> pieces in place.
>
> Alternative design:
> crrev.com/c/2309759 previously attempted this refactor by splitting the
> registration process into a shared app-level API instead of a Utils API.
> As a result, ShellUtil functions that are currently atomic
> (AddFileAssociations) were split into non-atomic app-level and file
> handler pieces. The design was abandoned for this reason.
>
> [1] https://docs.google.com/document/d/1NHlWLjAPZ-dyxcz3AoTWibeerDeHW7Vqrx6FmhB0XmE/edit#heading=h.qsh70q
>
> Bug: 1019239
> Change-Id: I5f727a5ca1483efd21935c0201ee54474a94e598
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2462383
> Reviewed-by: Scott Violet <sky@chromium.org>
> Reviewed-by: Daniel Murphy <dmurph@chromium.org>
> Reviewed-by: David Bienvenu <davidbienvenu@chromium.org>
> Reviewed-by: Greg Thompson <grt@chromium.org>
> Commit-Queue: Fabio Rocha <fabio.rocha@microsoft.com>
> Cr-Commit-Position: refs/heads/master@{#818065}

Bug: 1019239
Change-Id: I06cfeb90e2e3c7b438210250fc95917e4362e425
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2482185Reviewed-by: default avatarGreg Thompson <grt@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarDaniel Murphy <dmurph@chromium.org>
Reviewed-by: default avatarDavid Bienvenu <davidbienvenu@chromium.org>
Commit-Queue: Fabio Rocha <fabio.rocha@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#818684}
parent e38e44f1
......@@ -58,7 +58,7 @@
#include "chrome/browser/web_applications/chrome_pwa_launcher/last_browser_file_util.h"
#include "chrome/browser/web_applications/chrome_pwa_launcher/launcher_log_reporter.h"
#include "chrome/browser/web_applications/chrome_pwa_launcher/launcher_update.h"
#include "chrome/browser/web_applications/components/web_app_file_handler_registration_win.h"
#include "chrome/browser/web_applications/components/web_app_handler_registration_utils_win.h"
#include "chrome/browser/web_applications/components/web_app_shortcut.h"
#include "chrome/browser/win/browser_util.h"
#include "chrome/browser/win/chrome_elf_init.h"
......
......@@ -110,7 +110,8 @@ source_set("components") {
if (is_win) {
sources += [
"web_app_file_handler_registration_win.cc",
"web_app_file_handler_registration_win.h",
"web_app_handler_registration_utils_win.cc",
"web_app_handler_registration_utils_win.h",
"web_app_run_on_os_login_win.cc",
"web_app_shortcut_win.cc",
"web_app_shortcut_win.h",
......@@ -176,6 +177,7 @@ source_set("unit_tests") {
if (is_win) {
sources += [
"web_app_file_handler_registration_win_unittest.cc",
"web_app_handler_registration_utils_win_unittest.cc",
"web_app_run_on_os_login_win_unittest.cc",
]
}
......
// 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.
#ifndef CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_FILE_HANDLER_REGISTRATION_WIN_H_
#define CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_FILE_HANDLER_REGISTRATION_WIN_H_
#include "chrome/browser/web_applications/components/web_app_file_handler_registration.h"
#include "base/files/file_path.h"
#include "base/strings/string16.h"
#include "chrome/browser/web_applications/components/web_app_id.h"
namespace web_app {
// Returns the app-specific-launcher filename to be used for |app_name|.
base::FilePath GetAppSpecificLauncherFilename(const base::string16& app_name);
// Returns the Windows ProgId for the web app with the passed |app_id| in
// |profile_path|.
base::string16 GetProgIdForApp(const base::FilePath& profile_path,
const AppId& app_id);
} // namespace web_app
#endif // CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_FILE_HANDLER_REGISTRATION_WIN_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Copyright 2020 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/web_applications/components/web_app_file_handler_registration_win.h"
#include "chrome/browser/web_applications/components/web_app_file_handler_registration.h"
#include <set>
#include <string>
......@@ -20,6 +20,7 @@
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/web_applications/chrome_pwa_launcher/chrome_pwa_launcher_util.h"
#include "chrome/browser/web_applications/components/web_app_handler_registration_utils_win.h"
#include "chrome/browser/web_applications/test/test_file_handler_manager.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/installer/util/shell_util.h"
......@@ -114,6 +115,9 @@ class WebAppFileHandlerRegistrationWinTest : public testing::Test {
RegisterFileHandlersWithOs(app_id(), app_name, profile, file_handlers);
base::ThreadPoolInstance::Get()->FlushForTesting();
base::RunLoop().RunUntilIdle();
base::ThreadPoolInstance::Get()->FlushForTesting();
base::FilePath registered_app_path = ShellUtil::GetApplicationPathForProgId(
GetProgIdForApp(profile->GetPath(), app_id()));
EXPECT_TRUE(base::PathExists(registered_app_path));
......@@ -159,28 +163,6 @@ class WebAppFileHandlerRegistrationWinTest : public testing::Test {
const AppId app_id_ = "app_id";
};
// Test various attributes of ProgIds returned by GetAppIdForApp.
TEST_F(WebAppFileHandlerRegistrationWinTest, GetProgIdForApp) {
// Create a long app_id and verify that the prog id is less
// than 39 characters, and only contains alphanumeric characters and
// non leading '.'s See
// https://docs.microsoft.com/en-us/windows/win32/com/-progid--key.
AppId app_id1("app_id12345678901234567890123456789012345678901234");
constexpr unsigned int kMaxProgIdLen = 39;
base::string16 prog_id1 = GetProgIdForApp(profile()->GetPath(), app_id1);
EXPECT_LE(prog_id1.length(), kMaxProgIdLen);
for (auto itr = prog_id1.begin(); itr != prog_id1.end(); itr++)
EXPECT_TRUE(std::isalnum(*itr) || (*itr == '.' && itr != prog_id1.begin()));
AppId app_id2("different_appid");
// Check that different app ids in the same profile have different
// prog ids.
EXPECT_NE(prog_id1, GetProgIdForApp(profile()->GetPath(), app_id2));
// Create a different profile, and verify that the prog id for the same
// app_id in a different profile is different.
TestingProfile profile2;
EXPECT_NE(prog_id1, GetProgIdForApp(profile2.GetPath(), app_id1));
}
TEST_F(WebAppFileHandlerRegistrationWinTest, RegisterFileHandlersForWebApp) {
AddAndVerifyFileAssociations(profile(), kAppName, "");
......@@ -259,6 +241,8 @@ TEST_F(WebAppFileHandlerRegistrationWinTest,
UnregisterFileHandlersWithOs(app_id(), profile());
base::ThreadPoolInstance::Get()->FlushForTesting();
base::RunLoop().RunUntilIdle();
base::ThreadPoolInstance::Get()->FlushForTesting();
EXPECT_FALSE(base::PathExists(app_specific_launcher_path));
// Verify that "(Profile 2)" was removed from the web app launcher and
// file association registry entries.
......@@ -306,6 +290,8 @@ TEST_F(WebAppFileHandlerRegistrationWinTest,
UnregisterFileHandlersWithOs(app_id(), profile());
base::ThreadPoolInstance::Get()->FlushForTesting();
base::RunLoop().RunUntilIdle();
base::ThreadPoolInstance::Get()->FlushForTesting();
EXPECT_FALSE(base::PathExists(app_specific_launcher_path));
// Verify that "(Profile 2)" was not removed from the web app launcher and
// file association registry entries.
......@@ -338,6 +324,7 @@ TEST_F(WebAppFileHandlerRegistrationWinTest, UnregisterFileHandlersForWebApp) {
UnregisterFileHandlersWithOs(app_id(), profile());
base::ThreadPoolInstance::Get()->FlushForTesting();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(base::PathExists(app_specific_launcher_path));
EXPECT_FALSE(ProgIdRegisteredForFileExtension(".txt", app_id(), profile()));
EXPECT_FALSE(ProgIdRegisteredForFileExtension(".doc", app_id(), profile()));
......@@ -348,66 +335,4 @@ TEST_F(WebAppFileHandlerRegistrationWinTest, UnregisterFileHandlersForWebApp) {
EXPECT_TRUE(file_associations_and_app_name.app_name.empty());
}
// Test that invalid file name characters in app_name are replaced with '_'.
TEST_F(WebAppFileHandlerRegistrationWinTest, AppNameWithInvalidChars) {
// '*' is an invalid char in Windows file names, so it should be replaced
// with '_'.
std::string app_name("app*name");
base::FilePath app_specific_launcher_path =
GetLauncherPathForApp(profile(), app_id(), "app_name");
apps::FileHandlers file_handlers =
GetFileHandlersWithFileExtensions({".txt"});
RegisterFileHandlersWithOs(app_id(), app_name, profile(), file_handlers);
base::ThreadPoolInstance::Get()->FlushForTesting();
base::FilePath registered_app_path = ShellUtil::GetApplicationPathForProgId(
GetProgIdForApp(profile()->GetPath(), app_id()));
EXPECT_FALSE(registered_app_path.empty());
EXPECT_TRUE(base::PathExists(app_specific_launcher_path));
EXPECT_EQ(app_specific_launcher_path, registered_app_path);
}
// Test that an app name that is a reserved filename on Windows has '_'
// prepended to it when used as a filename for its launcher.
TEST_F(WebAppFileHandlerRegistrationWinTest, AppNameIsReservedFilename) {
// "con" is a reserved filename on Windows, so it should have '_' prepended.
std::string app_name("con");
base::FilePath app_specific_launcher_path =
GetLauncherPathForApp(profile(), app_id(), "_con");
apps::FileHandlers file_handlers =
GetFileHandlersWithFileExtensions({".txt"});
RegisterFileHandlersWithOs(app_id(), app_name, profile(), file_handlers);
base::ThreadPoolInstance::Get()->FlushForTesting();
base::FilePath registered_app_path = ShellUtil::GetApplicationPathForProgId(
GetProgIdForApp(profile()->GetPath(), app_id()));
EXPECT_FALSE(registered_app_path.empty());
EXPECT_TRUE(base::PathExists(app_specific_launcher_path));
EXPECT_EQ(app_specific_launcher_path, registered_app_path);
}
// Test that an app name containing '.' characters has them replaced with '_' on
// Windows 7 when used as a filename for its launcher.
TEST_F(WebAppFileHandlerRegistrationWinTest, AppNameContainsDot) {
// "some.app.name" should become "some_app_name" on Windows 7.
std::string app_name("some.app.name");
base::FilePath app_specific_launcher_path = GetLauncherPathForApp(
profile(), app_id(),
base::win::GetVersion() > base::win::Version::WIN7 ? "some.app.name"
: "some_app_name");
apps::FileHandlers file_handlers =
GetFileHandlersWithFileExtensions({".txt"});
RegisterFileHandlersWithOs(app_id(), app_name, profile(), file_handlers);
base::ThreadPoolInstance::Get()->FlushForTesting();
base::FilePath registered_app_path = ShellUtil::GetApplicationPathForProgId(
GetProgIdForApp(profile()->GetPath(), app_id()));
EXPECT_FALSE(registered_app_path.empty());
EXPECT_TRUE(base::PathExists(app_specific_launcher_path));
EXPECT_EQ(app_specific_launcher_path, registered_app_path);
}
} // namespace web_app
// Copyright 2020 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.
#ifndef CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_HANDLER_REGISTRATION_UTILS_WIN_H_
#define CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_HANDLER_REGISTRATION_UTILS_WIN_H_
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/optional.h"
#include "chrome/browser/web_applications/components/web_app_id.h"
#include "chrome/browser/web_applications/components/web_app_shortcut_win.h"
namespace web_app {
base::CommandLine GetAppLauncherCommand(const AppId& app_id,
const base::FilePath& app_launcher_path,
const base::FilePath& profile_path);
// Returns the extension required for new installations of |app_id| based on
// current state of duplicate installations of |app_id| in other profiles.
base::string16 GetAppNameExtensionForNextInstall(
const AppId& app_id,
const base::FilePath& profile_path);
base::FilePath GetAppSpecificLauncherFilename(const base::string16& app_name);
// See https://docs.microsoft.com/en-us/windows/win32/com/-progid--key for
// the allowed characters in a prog_id. Since the prog_id is stored in the
// Windows registry, the mapping between a given profile+app_id and a prog_id
// can not be changed.
base::string16 GetProgIdForApp(const base::FilePath& profile_path,
const AppId& app_id);
// Makes an app-specific copy of chrome_pwa_launcher.exe that lives in the web
// application directory. Returns path of the launcher file if successful,
// base::nullopt otherwise.
base::Optional<base::FilePath> CreateAppLauncherFile(
const AppId& app_id,
const base::string16& app_name,
const base::string16& app_name_extension,
const base::FilePath& profile_path);
// Checks if there is an installation of this app in another profile that needs
// to be updated with a profile specific name and executes required update.
void CheckAndUpdateExternalInstallations(const base::FilePath& cur_profile_path,
const AppId& app_id);
// Result of file handler registration process.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class RegistrationResult {
kSuccess = 0,
kFailToCopyFromGenericLauncher = 1,
kFailToAddFileAssociation = 2,
kFailToDeleteExistingRegistration = 3,
kFailToDeleteFileAssociationsForExistingRegistration = 4,
kMaxValue = kFailToDeleteFileAssociationsForExistingRegistration
};
// Record UMA metric for the result of file handler registration.
void RecordRegistration(RegistrationResult result);
} // namespace web_app
#endif // CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_WEB_APP_HANDLER_REGISTRATION_UTILS_WIN_H_
......@@ -2511,39 +2511,80 @@ bool ShellUtil::DeleteFileAssociations(const base::string16& prog_id) {
WorkItem::kWow64Default);
}
// static
bool ShellUtil::AddApplicationClass(
const base::string16& prog_id,
const base::CommandLine& shell_open_command_line,
const base::string16& application_name,
const base::string16& application_description,
const base::FilePath& icon_path) {
ApplicationInfo app_info;
app_info.prog_id = prog_id;
app_info.file_type_name = application_description;
app_info.file_type_icon_path = icon_path;
app_info.command_line =
shell_open_command_line.GetCommandLineStringForShell();
app_info.application_name = application_name;
app_info.application_icon_path = icon_path;
app_info.application_icon_index = 0;
std::vector<std::unique_ptr<RegistryEntry>> entries;
GetProgIdEntries(app_info, &entries);
return AreEntriesAsDesired(entries, RegistryEntry::LOOK_IN_HKCU) ||
AddRegistryEntries(HKEY_CURRENT_USER, entries);
}
// static
bool ShellUtil::DeleteApplicationClass(const base::string16& prog_id) {
base::string16 prog_id_path(kRegClasses);
prog_id_path.push_back(base::FilePath::kSeparators[0]);
prog_id_path.append(prog_id);
// Delete the key HKEY_CURRENT_USER\Software\Classes\|prog_id|.
return InstallUtil::DeleteRegistryKey(HKEY_CURRENT_USER, prog_id_path,
WorkItem::kWow64Default);
}
// static
ShellUtil::FileAssociationsAndAppName ShellUtil::GetFileAssociationsAndAppName(
const base::string16& prog_id) {
FileAssociationsAndAppName file_associations_and_app_name;
// Get list of handled file extensions from value FileExtensions at
// HKEY_CURRENT_USER\Software\Classes\|prog_id|.
base::string16 prog_id_path(kRegClasses);
prog_id_path.push_back(base::FilePath::kSeparators[0]);
prog_id_path.append(prog_id);
// Get the app name from value ApplicationName at
// HKEY_CURRENT_USER\Software\Classes\|prog_id|\Application.
base::string16 application_path = prog_id_path + kRegApplication;
RegKey application_key(HKEY_CURRENT_USER, application_path.c_str(),
KEY_QUERY_VALUE);
if (application_key.ReadValue(kRegApplicationName,
&file_associations_and_app_name.app_name) !=
ERROR_SUCCESS) {
return file_associations_and_app_name;
}
// If present, Get list of handled file extensions from value FileExtensions
// at HKEY_CURRENT_USER\Software\Classes\|prog_id|.
RegKey file_extensions_key(HKEY_CURRENT_USER, prog_id_path.c_str(),
KEY_QUERY_VALUE);
base::string16 handled_file_extensions;
if (file_extensions_key.ReadValue(
L"FileExtensions", &handled_file_extensions) != ERROR_SUCCESS) {
return FileAssociationsAndAppName();
}
std::vector<base::StringPiece16> file_associations_vec =
base::SplitStringPiece(base::StringPiece16(handled_file_extensions),
base::StringPiece16(L";"), base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
for (const auto& file_extension : file_associations_vec) {
// Skip over the leading '.' so that we return the same
// extensions as were passed to AddFileAssociations.
file_associations_and_app_name.file_associations.emplace(
file_extension.substr(1));
}
prog_id_path.append(kRegApplication);
RegKey prog_id_key(HKEY_CURRENT_USER, prog_id_path.c_str(), KEY_QUERY_VALUE);
if (prog_id_key.ReadValue(kRegApplicationName,
&file_associations_and_app_name.app_name) !=
ERROR_SUCCESS) {
return FileAssociationsAndAppName();
L"FileExtensions", &handled_file_extensions) == ERROR_SUCCESS) {
std::vector<base::StringPiece16> file_associations_vec =
base::SplitStringPiece(base::StringPiece16(handled_file_extensions),
base::StringPiece16(STRING16_LITERAL(";")),
base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
for (const auto& file_extension : file_associations_vec) {
// Skip over the leading '.' so that we return the same
// extensions as were passed to AddFileAssociations.
file_associations_and_app_name.file_associations.emplace(
file_extension.substr(1));
}
}
return file_associations_and_app_name;
}
......
......@@ -660,6 +660,31 @@ class ShellUtil {
// with this name will be deleted.
static bool DeleteFileAssociations(const base::string16& prog_id);
// Adds an application entry and metadata sub-entries to
// HKCU\SOFTWARE\classes\<prog_id> capable of handling file type /
// protocol associations.
//
// |prog_id| is the ProgId used by Windows to uniquely identity this
// application. Must not be empty or start with a '.'.
// |shell_open_command_line| is the command to execute when opening the app
// via association.
// |application_name| is the friendly name displayed for this application in
// the Open With menu.
// |application_description| is the description for this application to be
// displayed by certain Windows settings dialogs.
// |icon_path| is the path of the icon displayed for this application in the
// Open With menu, and used for default files / protocols associated with this
// application.
static bool AddApplicationClass(
const base::string16& prog_id,
const base::CommandLine& shell_open_command_line,
const base::string16& application_name,
const base::string16& application_description,
const base::FilePath& icon_path);
// Removes all entries of an application at HKCU\SOFTWARE\classes\<prog_id>.
static bool DeleteApplicationClass(const base::string16& prog_id);
// Returns the app name and file associations registered for a particular
// application in the Windows registry. If there is no entry in the registry
// for |prog_id|, nothing will be returned.
......
......@@ -973,6 +973,57 @@ TEST_F(ShellUtilRegistryTest, DeleteFileAssociations) {
EXPECT_EQ(L"SomeOtherApp", value);
}
TEST_F(ShellUtilRegistryTest, AddApplicationClass) {
// Add TestApp application class and verify registry entries.
EXPECT_TRUE(ShellUtil::AddApplicationClass(
base::string16(kTestProgid), OpenCommand(), kTestApplicationName,
kTestFileTypeName, base::FilePath(kTestIconPath)));
base::win::RegKey key;
std::wstring value;
ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER,
L"Software\\Classes\\TestApp", KEY_READ));
EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"", &value));
EXPECT_EQ(L"Test File Type", value);
ASSERT_EQ(ERROR_SUCCESS,
key.Open(HKEY_CURRENT_USER,
L"Software\\Classes\\TestApp\\DefaultIcon", KEY_READ));
EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"", &value));
EXPECT_EQ(L"D:\\test.ico,0", value);
ASSERT_EQ(
ERROR_SUCCESS,
key.Open(HKEY_CURRENT_USER,
L"Software\\Classes\\TestApp\\shell\\open\\command", KEY_READ));
EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"", &value));
EXPECT_EQ(L"\"C:\\test.exe\" --single-argument %1", value);
// The Application subkey and values are only required by Windows 8 and later.
if (base::win::GetVersion() >= base::win::Version::WIN8) {
ASSERT_EQ(ERROR_SUCCESS,
key.Open(HKEY_CURRENT_USER,
L"Software\\Classes\\TestApp\\Application", KEY_READ));
EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"ApplicationName", &value));
EXPECT_EQ(L"Test Application", value);
EXPECT_EQ(ERROR_SUCCESS, key.ReadValue(L"ApplicationIcon", &value));
EXPECT_EQ(L"D:\\test.ico,0", value);
}
}
TEST_F(ShellUtilRegistryTest, DeleteApplicationClass) {
ASSERT_TRUE(ShellUtil::AddApplicationClass(
kTestProgid, OpenCommand(), kTestApplicationName, kTestFileTypeName,
base::FilePath(kTestIconPath)));
base::win::RegKey key;
std::wstring value;
ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER,
L"Software\\Classes\\TestApp", KEY_READ));
EXPECT_TRUE(ShellUtil::DeleteApplicationClass(kTestProgid));
EXPECT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER,
L"Software\\Classes\\TestApp", KEY_READ));
}
TEST_F(ShellUtilRegistryTest, GetFileAssociationsAndAppName) {
ShellUtil::FileAssociationsAndAppName empty_file_associations_and_app_name(
ShellUtil::GetFileAssociationsAndAppName(kTestProgid));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment