|  | // Copyright (c) 2012 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/extensions/convert_web_app.h" | 
|  |  | 
|  | #include <stddef.h> | 
|  |  | 
|  | #include <map> | 
|  | #include <memory> | 
|  | #include <string> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "base/files/file_path.h" | 
|  | #include "base/files/file_util.h" | 
|  | #include "base/files/scoped_temp_dir.h" | 
|  | #include "base/path_service.h" | 
|  | #include "base/run_loop.h" | 
|  | #include "base/stl_util.h" | 
|  | #include "base/strings/stringprintf.h" | 
|  | #include "base/strings/utf_string_conversions.h" | 
|  | #include "base/time/time.h" | 
|  | #include "base/version.h" | 
|  | #include "chrome/browser/extensions/extension_service.h" | 
|  | #include "chrome/browser/extensions/extension_service_test_base.h" | 
|  | #include "chrome/browser/web_applications/components/web_application_info.h" | 
|  | #include "chrome/common/chrome_features.h" | 
|  | #include "chrome/common/chrome_paths.h" | 
|  | #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" | 
|  | #include "chrome/common/extensions/manifest_handlers/linked_app_icons.h" | 
|  | #include "components/services/app_service/public/cpp/file_handler.h" | 
|  | #include "components/services/app_service/public/cpp/file_handler_info.h" | 
|  | #include "extensions/browser/extension_system.h" | 
|  | #include "extensions/common/extension.h" | 
|  | #include "extensions/common/extension_icon_set.h" | 
|  | #include "extensions/common/extension_resource.h" | 
|  | #include "extensions/common/manifest_constants.h" | 
|  | #include "extensions/common/manifest_handlers/file_handler_info.h" | 
|  | #include "extensions/common/manifest_handlers/icons_handler.h" | 
|  | #include "extensions/common/manifest_handlers/web_app_file_handler.h" | 
|  | #include "extensions/common/manifest_handlers/web_app_linked_shortcut_items.h" | 
|  | #include "extensions/common/manifest_handlers/web_app_shortcut_icons_handler.h" | 
|  | #include "extensions/common/permissions/permission_set.h" | 
|  | #include "extensions/common/permissions/permissions_data.h" | 
|  | #include "extensions/common/url_pattern.h" | 
|  | #include "testing/gmock/include/gmock/gmock-matchers.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  | #include "ui/gfx/codec/png_codec.h" | 
|  | #include "url/gurl.h" | 
|  |  | 
|  | using extensions::mojom::ManifestLocation; | 
|  |  | 
|  | namespace extensions { | 
|  |  | 
|  | namespace keys = manifest_keys; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Returns an icon bitmap corresponding to a canned icon size. | 
|  | SkBitmap GetIconBitmap(int size) { | 
|  | SkBitmap result; | 
|  |  | 
|  | base::FilePath icon_file; | 
|  | if (!base::PathService::Get(chrome::DIR_TEST_DATA, &icon_file)) { | 
|  | ADD_FAILURE() << "Could not get test data directory."; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | icon_file = icon_file.AppendASCII("extensions") | 
|  | .AppendASCII("convert_web_app") | 
|  | .AppendASCII(base::StringPrintf("%i.png", size)); | 
|  |  | 
|  | std::string icon_data; | 
|  | if (!base::ReadFileToString(icon_file, &icon_data)) { | 
|  | ADD_FAILURE() << "Could not read test icon."; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | if (!gfx::PNGCodec::Decode( | 
|  | reinterpret_cast<const unsigned char*>(icon_data.c_str()), | 
|  | icon_data.size(), &result)) { | 
|  | ADD_FAILURE() << "Could not decode test icon."; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | base::Time GetTestTime(int year, int month, int day, int hour, int minute, | 
|  | int second, int millisecond) { | 
|  | base::Time::Exploded exploded = {0}; | 
|  | exploded.year = year; | 
|  | exploded.month = month; | 
|  | exploded.day_of_month = day; | 
|  | exploded.hour = hour; | 
|  | exploded.minute = minute; | 
|  | exploded.second = second; | 
|  | exploded.millisecond = millisecond; | 
|  | base::Time out_time; | 
|  | EXPECT_TRUE(base::Time::FromUTCExploded(exploded, &out_time)); | 
|  | return out_time; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class ExtensionFromWebApp : public extensions::ExtensionServiceTestBase { | 
|  | public: | 
|  | void SetUp() override { | 
|  | extensions::ExtensionServiceTestBase::SetUp(); | 
|  | ASSERT_TRUE(extensions_dir_.CreateUniqueTempDir()); | 
|  | } | 
|  |  | 
|  | void StartExtensionService() { | 
|  | InitializeEmptyExtensionService(); | 
|  | service()->Init(); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | ASSERT_TRUE(ExtensionSystem::Get(service()->profile())->is_ready()); | 
|  | } | 
|  |  | 
|  | const base::FilePath& ExtensionPath() const { | 
|  | return extensions_dir_.GetPath(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | base::ScopedTempDir extensions_dir_; | 
|  | }; | 
|  |  | 
|  | class ExtensionFromWebAppWithShortcutsMenu : public ExtensionFromWebApp { | 
|  | public: | 
|  | ExtensionFromWebAppWithShortcutsMenu() { | 
|  | feature_list_.InitAndEnableFeature( | 
|  | features::kDesktopPWAsAppIconShortcutsMenu); | 
|  | } | 
|  | }; | 
|  |  | 
|  | TEST_F(ExtensionFromWebApp, GetScopeURLFromBookmarkApp) { | 
|  | StartExtensionService(); | 
|  | base::DictionaryValue manifest; | 
|  | manifest.SetString(keys::kName, "Test App"); | 
|  | manifest.SetString(keys::kVersion, "0"); | 
|  | manifest.SetString(keys::kLaunchWebURL, "http://aaronboodman.com/gearpad/"); | 
|  |  | 
|  | // Create a "url_handlers" dictionary with one URL handler generated from | 
|  | // the scope. | 
|  | // { | 
|  | //   "scope": { | 
|  | //     "matches": [ "http://aaronboodman.com/gearpad/*" ], | 
|  | //     "title": "Test App" | 
|  | //   }, | 
|  | // } | 
|  | GURL scope_url = GURL("http://aaronboodman.com/gearpad/"); | 
|  | manifest.SetDictionary(keys::kUrlHandlers, CreateURLHandlersForBookmarkApp( | 
|  | scope_url, u"Test App")); | 
|  |  | 
|  | std::string error; | 
|  | scoped_refptr<Extension> bookmark_app = | 
|  | Extension::Create(ExtensionPath(), ManifestLocation::kInternal, manifest, | 
|  | Extension::FROM_BOOKMARK, &error); | 
|  | ASSERT_TRUE(bookmark_app.get()); | 
|  |  | 
|  | EXPECT_EQ(scope_url, GetScopeURLFromBookmarkApp(bookmark_app.get())); | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionFromWebApp, GetScopeURLFromBookmarkApp_NoURLHandlers) { | 
|  | StartExtensionService(); | 
|  | base::DictionaryValue manifest; | 
|  | manifest.SetString(keys::kName, "Test App"); | 
|  | manifest.SetString(keys::kVersion, "0"); | 
|  | manifest.SetString(keys::kLaunchWebURL, "http://aaronboodman.com/gearpad/"); | 
|  | manifest.SetDictionary(keys::kUrlHandlers, | 
|  | std::make_unique<base::DictionaryValue>()); | 
|  |  | 
|  | std::string error; | 
|  | scoped_refptr<Extension> bookmark_app = | 
|  | Extension::Create(ExtensionPath(), ManifestLocation::kInternal, manifest, | 
|  | Extension::FROM_BOOKMARK, &error); | 
|  | ASSERT_TRUE(bookmark_app.get()); | 
|  |  | 
|  | EXPECT_EQ(GURL(), GetScopeURLFromBookmarkApp(bookmark_app.get())); | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionFromWebApp, GetScopeURLFromBookmarkApp_WrongURLHandler) { | 
|  | StartExtensionService(); | 
|  | base::DictionaryValue manifest; | 
|  | manifest.SetString(keys::kName, "Test App"); | 
|  | manifest.SetString(keys::kVersion, "0"); | 
|  | manifest.SetString(keys::kLaunchWebURL, "http://aaronboodman.com/gearpad/"); | 
|  |  | 
|  | // Create a "url_handlers" dictionary with one URL handler not generated | 
|  | // from the scope. | 
|  | // { | 
|  | //   "test_url_handler": { | 
|  | //     "matches": [ "http://*.aaronboodman.com/" ], | 
|  | //     "title": "test handler" | 
|  | //   } | 
|  | // } | 
|  | auto test_matches = std::make_unique<base::ListValue>(); | 
|  | test_matches->AppendString("http://*.aaronboodman.com/"); | 
|  |  | 
|  | auto test_handler = std::make_unique<base::DictionaryValue>(); | 
|  | test_handler->SetList(keys::kMatches, std::move(test_matches)); | 
|  | test_handler->SetString(keys::kUrlHandlerTitle, "test handler"); | 
|  |  | 
|  | auto url_handlers = std::make_unique<base::DictionaryValue>(); | 
|  | url_handlers->SetDictionary("test_url_handler", std::move(test_handler)); | 
|  | manifest.SetDictionary(keys::kUrlHandlers, std::move(url_handlers)); | 
|  |  | 
|  | std::string error; | 
|  | scoped_refptr<Extension> bookmark_app = | 
|  | Extension::Create(ExtensionPath(), ManifestLocation::kInternal, manifest, | 
|  | Extension::FROM_BOOKMARK, &error); | 
|  | ASSERT_TRUE(bookmark_app.get()); | 
|  |  | 
|  | EXPECT_EQ(GURL(), GetScopeURLFromBookmarkApp(bookmark_app.get())); | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionFromWebApp, GetScopeURLFromBookmarkApp_ExtraURLHandler) { | 
|  | StartExtensionService(); | 
|  | base::DictionaryValue manifest; | 
|  | manifest.SetString(keys::kName, "Test App"); | 
|  | manifest.SetString(keys::kVersion, "0"); | 
|  | manifest.SetString(keys::kLaunchWebURL, "http://aaronboodman.com/gearpad/"); | 
|  |  | 
|  | // Create a "url_handlers" dictionary with two URL handlers. One for | 
|  | // the scope and and extra one for testing. | 
|  | // { | 
|  | //   "scope": { | 
|  | //     "matches": [ "http://aaronboodman.com/gearpad/*" ], | 
|  | //     "title": "Test App" | 
|  | //   }, | 
|  | //   "test_url_handler": { | 
|  | //     "matches": [ "http://*.aaronboodman.com/" ], | 
|  | //     "title": "test handler" | 
|  | //   } | 
|  | // } | 
|  | GURL scope_url = GURL("http://aaronboodman.com/gearpad/"); | 
|  | std::unique_ptr<base::DictionaryValue> url_handlers = | 
|  | CreateURLHandlersForBookmarkApp(scope_url, u"Test App"); | 
|  |  | 
|  | auto test_matches = std::make_unique<base::ListValue>(); | 
|  | test_matches->AppendString("http://*.aaronboodman.com/"); | 
|  |  | 
|  | auto test_handler = std::make_unique<base::DictionaryValue>(); | 
|  | test_handler->SetList(keys::kMatches, std::move(test_matches)); | 
|  | test_handler->SetString(keys::kUrlHandlerTitle, "test handler"); | 
|  |  | 
|  | url_handlers->SetDictionary("test_url_handler", std::move(test_handler)); | 
|  | manifest.SetDictionary(keys::kUrlHandlers, std::move(url_handlers)); | 
|  |  | 
|  | std::string error; | 
|  | scoped_refptr<Extension> bookmark_app = | 
|  | Extension::Create(ExtensionPath(), ManifestLocation::kInternal, manifest, | 
|  | Extension::FROM_BOOKMARK, &error); | 
|  | ASSERT_TRUE(bookmark_app.get()); | 
|  |  | 
|  | // Check that we can retrieve the scope even if there is an extra | 
|  | // url handler. | 
|  | EXPECT_EQ(scope_url, GetScopeURLFromBookmarkApp(bookmark_app.get())); | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionFromWebApp, GenerateVersion) { | 
|  | StartExtensionService(); | 
|  | EXPECT_EQ("2010.1.1.0", | 
|  | ConvertTimeToExtensionVersion( | 
|  | GetTestTime(2010, 1, 1, 0, 0, 0, 0))); | 
|  | EXPECT_EQ("2010.12.31.22111", | 
|  | ConvertTimeToExtensionVersion( | 
|  | GetTestTime(2010, 12, 31, 8, 5, 50, 500))); | 
|  | EXPECT_EQ("2010.10.1.65535", | 
|  | ConvertTimeToExtensionVersion( | 
|  | GetTestTime(2010, 10, 1, 23, 59, 59, 999))); | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionFromWebApp, Basic) { | 
|  | StartExtensionService(); | 
|  | WebApplicationInfo web_app; | 
|  | web_app.title = u"Gearpad"; | 
|  | web_app.description = u"The best text editor in the universe!"; | 
|  | web_app.start_url = GURL("http://aaronboodman.com/gearpad/"); | 
|  | web_app.scope = GURL("http://aaronboodman.com/gearpad/"); | 
|  |  | 
|  | const int sizes[] = {16, 48, 128}; | 
|  | for (size_t i = 0; i < base::size(sizes); ++i) { | 
|  | WebApplicationIconInfo icon_info; | 
|  | icon_info.url = | 
|  | web_app.start_url.Resolve(base::StringPrintf("%i.png", sizes[i])); | 
|  | icon_info.square_size_px = sizes[i]; | 
|  | web_app.icon_infos.push_back(std::move(icon_info)); | 
|  | web_app.icon_bitmaps.any[sizes[i]] = GetIconBitmap(sizes[i]); | 
|  | } | 
|  |  | 
|  | scoped_refptr<Extension> extension = ConvertWebAppToExtension( | 
|  | web_app, GetTestTime(1978, 12, 11, 0, 0, 0, 0), ExtensionPath(), | 
|  | Extension::NO_FLAGS, ManifestLocation::kInternal); | 
|  | ASSERT_TRUE(extension.get()); | 
|  |  | 
|  | base::ScopedTempDir extension_dir; | 
|  | EXPECT_TRUE(extension_dir.Set(extension->path())); | 
|  |  | 
|  | EXPECT_TRUE(extension->is_app()); | 
|  | EXPECT_TRUE(extension->is_hosted_app()); | 
|  | EXPECT_TRUE(extension->from_bookmark()); | 
|  | EXPECT_FALSE(extension->is_legacy_packaged_app()); | 
|  |  | 
|  | EXPECT_FALSE(extension->was_installed_by_default()); | 
|  | EXPECT_FALSE(extension->was_installed_by_oem()); | 
|  | EXPECT_FALSE(extension->from_webstore()); | 
|  | EXPECT_EQ(ManifestLocation::kInternal, extension->location()); | 
|  |  | 
|  | EXPECT_EQ("zVvdNZy3Mp7CFU8JVSyXNlDuHdVLbP7fDO3TGVzj/0w=", | 
|  | extension->public_key()); | 
|  | EXPECT_EQ("oplhagaaipaimkjlbekcdjkffijdockj", extension->id()); | 
|  | EXPECT_EQ("1978.12.11.0", extension->version().GetString()); | 
|  | EXPECT_EQ(base::UTF16ToUTF8(web_app.title), extension->name()); | 
|  | EXPECT_EQ(base::UTF16ToUTF8(web_app.description), extension->description()); | 
|  | EXPECT_EQ(web_app.start_url, | 
|  | AppLaunchInfo::GetFullLaunchURL(extension.get())); | 
|  | EXPECT_EQ(web_app.scope, GetScopeURLFromBookmarkApp(extension.get())); | 
|  | EXPECT_EQ(0u, | 
|  | extension->permissions_data()->active_permissions().apis().size()); | 
|  | ASSERT_EQ(0u, extension->web_extent().patterns().size()); | 
|  |  | 
|  | const LinkedAppIcons& linked_icons = | 
|  | LinkedAppIcons::GetLinkedAppIcons(extension.get()); | 
|  | EXPECT_EQ(web_app.icon_infos.size(), linked_icons.icons.size()); | 
|  | for (size_t i = 0; i < web_app.icon_infos.size(); ++i) { | 
|  | EXPECT_EQ(web_app.icon_infos[i].url, linked_icons.icons[i].url); | 
|  | EXPECT_EQ(web_app.icon_infos[i].square_size_px, linked_icons.icons[i].size); | 
|  | } | 
|  |  | 
|  | EXPECT_EQ(web_app.icon_bitmaps.any.size(), | 
|  | IconsInfo::GetIcons(extension.get()).map().size()); | 
|  | for (const std::pair<const SquareSizePx, SkBitmap>& icon : | 
|  | web_app.icon_bitmaps.any) { | 
|  | int size = icon.first; | 
|  | EXPECT_EQ(base::StringPrintf("icons/%i.png", size), | 
|  | IconsInfo::GetIcons(extension.get()) | 
|  | .Get(size, ExtensionIconSet::MATCH_EXACTLY)); | 
|  | ExtensionResource resource = IconsInfo::GetIconResource( | 
|  | extension.get(), size, ExtensionIconSet::MATCH_EXACTLY); | 
|  | ASSERT_TRUE(!resource.empty()); | 
|  | EXPECT_TRUE(base::PathExists(resource.GetFilePath())); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionFromWebApp, Minimal) { | 
|  | StartExtensionService(); | 
|  | WebApplicationInfo web_app; | 
|  | web_app.title = u"Gearpad"; | 
|  | web_app.start_url = GURL("http://aaronboodman.com/gearpad/"); | 
|  |  | 
|  | scoped_refptr<Extension> extension = ConvertWebAppToExtension( | 
|  | web_app, GetTestTime(1978, 12, 11, 0, 0, 0, 0), ExtensionPath(), | 
|  | Extension::NO_FLAGS, ManifestLocation::kInternal); | 
|  | ASSERT_TRUE(extension.get()); | 
|  |  | 
|  | base::ScopedTempDir extension_dir; | 
|  | EXPECT_TRUE(extension_dir.Set(extension->path())); | 
|  |  | 
|  | EXPECT_TRUE(extension->is_app()); | 
|  | EXPECT_TRUE(extension->is_hosted_app()); | 
|  | EXPECT_TRUE(extension->from_bookmark()); | 
|  | EXPECT_FALSE(extension->is_legacy_packaged_app()); | 
|  |  | 
|  | EXPECT_FALSE(extension->was_installed_by_default()); | 
|  | EXPECT_FALSE(extension->was_installed_by_oem()); | 
|  | EXPECT_FALSE(extension->from_webstore()); | 
|  | EXPECT_EQ(ManifestLocation::kInternal, extension->location()); | 
|  |  | 
|  | EXPECT_EQ("zVvdNZy3Mp7CFU8JVSyXNlDuHdVLbP7fDO3TGVzj/0w=", | 
|  | extension->public_key()); | 
|  | EXPECT_EQ("oplhagaaipaimkjlbekcdjkffijdockj", extension->id()); | 
|  | EXPECT_EQ("1978.12.11.0", extension->version().GetString()); | 
|  | EXPECT_EQ(base::UTF16ToUTF8(web_app.title), extension->name()); | 
|  | EXPECT_EQ("", extension->description()); | 
|  | EXPECT_EQ(web_app.start_url, | 
|  | AppLaunchInfo::GetFullLaunchURL(extension.get())); | 
|  | EXPECT_TRUE(GetScopeURLFromBookmarkApp(extension.get()).is_empty()); | 
|  | EXPECT_EQ(0u, IconsInfo::GetIcons(extension.get()).map().size()); | 
|  | EXPECT_EQ(0u, | 
|  | extension->permissions_data()->active_permissions().apis().size()); | 
|  | ASSERT_EQ(0u, extension->web_extent().patterns().size()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionFromWebApp, ExtraInstallationFlags) { | 
|  | StartExtensionService(); | 
|  | WebApplicationInfo web_app; | 
|  | web_app.title = u"Gearpad"; | 
|  | web_app.start_url = GURL("http://aaronboodman.com/gearpad/"); | 
|  |  | 
|  | scoped_refptr<Extension> extension = ConvertWebAppToExtension( | 
|  | web_app, GetTestTime(1978, 12, 11, 0, 0, 0, 0), ExtensionPath(), | 
|  | Extension::FROM_WEBSTORE | Extension::WAS_INSTALLED_BY_OEM, | 
|  | ManifestLocation::kInternal); | 
|  | ASSERT_TRUE(extension.get()); | 
|  |  | 
|  | EXPECT_TRUE(extension->is_app()); | 
|  | EXPECT_TRUE(extension->is_hosted_app()); | 
|  | EXPECT_TRUE(extension->from_bookmark()); | 
|  | EXPECT_FALSE(extension->is_legacy_packaged_app()); | 
|  |  | 
|  | EXPECT_TRUE(extension->was_installed_by_oem()); | 
|  | EXPECT_TRUE(extension->from_webstore()); | 
|  | EXPECT_FALSE(extension->was_installed_by_default()); | 
|  | EXPECT_EQ(ManifestLocation::kInternal, extension->location()); | 
|  | } | 
|  |  | 
|  | TEST_F(ExtensionFromWebApp, ExternalPolicyLocation) { | 
|  | StartExtensionService(); | 
|  | WebApplicationInfo web_app; | 
|  | web_app.title = u"Gearpad"; | 
|  | web_app.start_url = GURL("http://aaronboodman.com/gearpad/"); | 
|  |  | 
|  | scoped_refptr<Extension> extension = ConvertWebAppToExtension( | 
|  | web_app, GetTestTime(1978, 12, 11, 0, 0, 0, 0), ExtensionPath(), | 
|  | Extension::NO_FLAGS, ManifestLocation::kExternalPolicy); | 
|  | ASSERT_TRUE(extension.get()); | 
|  |  | 
|  | EXPECT_TRUE(extension->is_app()); | 
|  | EXPECT_TRUE(extension->is_hosted_app()); | 
|  | EXPECT_TRUE(extension->from_bookmark()); | 
|  | EXPECT_FALSE(extension->is_legacy_packaged_app()); | 
|  |  | 
|  | EXPECT_EQ(mojom::ManifestLocation::kExternalPolicy, extension->location()); | 
|  | } | 
|  |  | 
|  | // Tests that a scope not ending in "/" works correctly. | 
|  | // The tested behavior is unexpected but is working correctly according | 
|  | // to the Web Manifest spec. https://github.com/w3c/manifest/issues/554 | 
|  | TEST_F(ExtensionFromWebApp, ScopeDoesNotEndInSlash) { | 
|  | StartExtensionService(); | 
|  | WebApplicationInfo web_app; | 
|  | web_app.title = u"Gearpad"; | 
|  | web_app.description = u"The best text editor in the universe!"; | 
|  | web_app.start_url = GURL("http://aaronboodman.com/gearpad/"); | 
|  | web_app.scope = GURL("http://aaronboodman.com/gear"); | 
|  |  | 
|  | scoped_refptr<Extension> extension = ConvertWebAppToExtension( | 
|  | web_app, GetTestTime(1978, 12, 11, 0, 0, 0, 0), ExtensionPath(), | 
|  | Extension::NO_FLAGS, ManifestLocation::kInternal); | 
|  | ASSERT_TRUE(extension.get()); | 
|  | EXPECT_EQ(web_app.scope, GetScopeURLFromBookmarkApp(extension.get())); | 
|  | } | 
|  |  | 
|  | // Tests that |file_handler| on the WebAppManifest is correctly converted | 
|  | // to |file_handlers| on an extension manifest. | 
|  | TEST_F(ExtensionFromWebApp, FileHandlersAreCorrectlyConverted) { | 
|  | StartExtensionService(); | 
|  | WebApplicationInfo web_app; | 
|  | web_app.title = u"Graphr"; | 
|  | web_app.description = u"A magical graphy thing"; | 
|  | web_app.start_url = GURL("https://graphr.n/"); | 
|  | web_app.scope = GURL("https://graphr.n/"); | 
|  |  | 
|  | { | 
|  | blink::Manifest::FileHandler graph; | 
|  | graph.action = GURL("https://graphr.n/open-graph/"); | 
|  | graph.name = u"Graph"; | 
|  | graph.accept[u"text/svg+xml"].push_back(u""); | 
|  | graph.accept[u"text/svg+xml"].push_back(u".svg"); | 
|  | web_app.file_handlers.push_back(graph); | 
|  |  | 
|  | blink::Manifest::FileHandler raw; | 
|  | raw.action = GURL("https://graphr.n/open-raw/"); | 
|  | raw.name = u"Raw"; | 
|  | raw.accept[u"text/csv"].push_back(u".csv"); | 
|  | web_app.file_handlers.push_back(raw); | 
|  | } | 
|  |  | 
|  | scoped_refptr<Extension> extension = ConvertWebAppToExtension( | 
|  | web_app, GetTestTime(1978, 12, 11, 0, 0, 0, 0), ExtensionPath(), | 
|  | Extension::NO_FLAGS, ManifestLocation::kInternal); | 
|  |  | 
|  | ASSERT_TRUE(extension.get()); | 
|  |  | 
|  | const std::vector<apps::FileHandlerInfo>* file_handler_infos = | 
|  | extensions::FileHandlers::GetFileHandlers(extension.get()); | 
|  |  | 
|  | ASSERT_TRUE(file_handler_infos); | 
|  | EXPECT_EQ(2u, file_handler_infos->size()); | 
|  |  | 
|  | { | 
|  | const apps::FileHandlerInfo& info = file_handler_infos->at(0); | 
|  | EXPECT_EQ("https://graphr.n/open-graph/", info.id); | 
|  | EXPECT_FALSE(info.include_directories); | 
|  | EXPECT_EQ(apps::file_handler_verbs::kOpenWith, info.verb); | 
|  | // Extensions should contain SVG, and only SVG | 
|  | EXPECT_THAT(info.extensions, testing::UnorderedElementsAre("svg")); | 
|  | // Mime types should contain text/svg+xml and only text/svg+xml | 
|  | EXPECT_THAT(info.types, testing::UnorderedElementsAre("text/svg+xml")); | 
|  | } | 
|  | { | 
|  | const apps::FileHandlerInfo& info = file_handler_infos->at(1); | 
|  | EXPECT_EQ("https://graphr.n/open-raw/", info.id); | 
|  | EXPECT_FALSE(info.include_directories); | 
|  | EXPECT_EQ(apps::file_handler_verbs::kOpenWith, info.verb); | 
|  | // Extensions should contain csv, and only csv | 
|  | EXPECT_THAT(info.extensions, testing::UnorderedElementsAre("csv")); | 
|  | // Mime types should contain text/csv and only text/csv | 
|  | EXPECT_THAT(info.types, testing::UnorderedElementsAre("text/csv")); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Tests that |file_handler| on the WebAppManifest is correctly converted | 
|  | // to |web_app_file_handlers| on an extension manifest. | 
|  | TEST_F(ExtensionFromWebApp, WebAppFileHandlersAreCorrectlyConverted) { | 
|  | StartExtensionService(); | 
|  | WebApplicationInfo web_app; | 
|  | web_app.title = u"Graphr"; | 
|  | web_app.description = u"A magical graphy thing."; | 
|  | web_app.start_url = GURL("https://graphr.n/"); | 
|  | web_app.scope = GURL("https://graphr.n"); | 
|  |  | 
|  | { | 
|  | blink::Manifest::FileHandler file_handler; | 
|  | file_handler.action = GURL("https://graphr.n/open-graph/"); | 
|  | file_handler.name = u"Graph"; | 
|  | file_handler.accept[u"text/svg+xml"].push_back(u""); | 
|  | file_handler.accept[u"text/svg+xml"].push_back(u".svg"); | 
|  | web_app.file_handlers.push_back(file_handler); | 
|  | } | 
|  | { | 
|  | blink::Manifest::FileHandler file_handler; | 
|  | file_handler.action = GURL("https://graphr.n/open-raw/"); | 
|  | file_handler.name = u"Raw"; | 
|  | file_handler.accept[u"text/csv"].push_back(u".csv"); | 
|  | web_app.file_handlers.push_back(file_handler); | 
|  | } | 
|  |  | 
|  | scoped_refptr<Extension> extension = ConvertWebAppToExtension( | 
|  | web_app, GetTestTime(1978, 12, 11, 0, 0, 0, 0), ExtensionPath(), | 
|  | Extension::NO_FLAGS, ManifestLocation::kInternal); | 
|  |  | 
|  | ASSERT_TRUE(extension.get()); | 
|  |  | 
|  | const apps::FileHandlers* file_handlers = | 
|  | extensions::WebAppFileHandlers::GetWebAppFileHandlers(extension.get()); | 
|  |  | 
|  | ASSERT_TRUE(file_handlers); | 
|  | EXPECT_EQ(2u, file_handlers->size()); | 
|  |  | 
|  | { | 
|  | const apps::FileHandler& file_handler = file_handlers->at(0); | 
|  | EXPECT_EQ("https://graphr.n/open-graph/", file_handler.action); | 
|  | EXPECT_EQ(1u, file_handler.accept.size()); | 
|  | EXPECT_EQ("text/svg+xml", file_handler.accept[0].mime_type); | 
|  | EXPECT_THAT(file_handler.accept[0].file_extensions, | 
|  | testing::UnorderedElementsAre(".svg")); | 
|  | } | 
|  | { | 
|  | const apps::FileHandler& file_handler = file_handlers->at(1); | 
|  | EXPECT_EQ("https://graphr.n/open-raw/", file_handler.action); | 
|  | EXPECT_EQ(1u, file_handler.accept.size()); | 
|  | EXPECT_EQ("text/csv", file_handler.accept[0].mime_type); | 
|  | EXPECT_THAT(file_handler.accept[0].file_extensions, | 
|  | testing::UnorderedElementsAre(".csv")); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Tests that |shortcuts_menu_item_infos| on the WebAppManifest is correctly | 
|  | // converted to |web_app_shortcut_icons| and |web_app_linked_shortcut_items| on | 
|  | // an extension manifest. | 
|  | TEST_F(ExtensionFromWebAppWithShortcutsMenu, | 
|  | WebAppShortcutIconsAreCorrectlyConverted) { | 
|  | StartExtensionService(); | 
|  | WebApplicationInfo web_app; | 
|  | WebApplicationShortcutsMenuItemInfo shortcut_item; | 
|  | web_app.title = u"Shortcut App"; | 
|  | web_app.description = u"We have shortcuts."; | 
|  | web_app.start_url = GURL("https://shortcut-app.io/"); | 
|  | web_app.scope = GURL("https://shortcut-app.io"); | 
|  |  | 
|  | shortcut_item.name = u"Shortcut 1"; | 
|  | shortcut_item.url = GURL("https://shortcut-app.io/shortcuts/shortcut1"); | 
|  | { | 
|  | IconBitmaps shortcut_icon_bitmaps; | 
|  | std::vector<WebApplicationShortcutsMenuItemInfo::Icon> icon_infos; | 
|  | const int sizes[] = {16, 128}; | 
|  | for (const auto& size : sizes) { | 
|  | WebApplicationShortcutsMenuItemInfo::Icon icon_info; | 
|  | icon_info.url = web_app.start_url.Resolve( | 
|  | base::StringPrintf("shortcut1/%i.png", size)); | 
|  | icon_info.square_size_px = size; | 
|  | icon_infos.push_back(std::move(icon_info)); | 
|  | shortcut_icon_bitmaps.any[size] = GetIconBitmap(size); | 
|  | } | 
|  | shortcut_item.SetShortcutIconInfosForPurpose(IconPurpose::ANY, | 
|  | std::move(icon_infos)); | 
|  | web_app.shortcuts_menu_icon_bitmaps.emplace_back( | 
|  | std::move(shortcut_icon_bitmaps)); | 
|  | } | 
|  | web_app.shortcuts_menu_item_infos.push_back(std::move(shortcut_item)); | 
|  |  | 
|  | shortcut_item.name = u"Shortcut 2"; | 
|  | shortcut_item.url = GURL("https://shortcut-app.io/shortcuts/shortcut2"); | 
|  | { | 
|  | IconBitmaps shortcut_icon_bitmaps; | 
|  | std::vector<WebApplicationShortcutsMenuItemInfo::Icon> icon_infos; | 
|  | const int sizes[] = {16, 48}; | 
|  | for (const auto& size : sizes) { | 
|  | WebApplicationShortcutsMenuItemInfo::Icon icon_info; | 
|  | icon_info.url = | 
|  | web_app.start_url.Resolve(base::StringPrintf("0/%i.png", size)); | 
|  | icon_info.square_size_px = size; | 
|  | icon_infos.push_back(std::move(icon_info)); | 
|  | shortcut_icon_bitmaps.any[size] = GetIconBitmap(size); | 
|  | } | 
|  | shortcut_item.SetShortcutIconInfosForPurpose(IconPurpose::ANY, | 
|  | std::move(icon_infos)); | 
|  | web_app.shortcuts_menu_icon_bitmaps.emplace_back( | 
|  | std::move(shortcut_icon_bitmaps)); | 
|  | } | 
|  | web_app.shortcuts_menu_item_infos.push_back(std::move(shortcut_item)); | 
|  |  | 
|  | scoped_refptr<Extension> extension = ConvertWebAppToExtension( | 
|  | web_app, GetTestTime(1978, 12, 11, 0, 0, 0, 0), ExtensionPath(), | 
|  | Extension::FROM_BOOKMARK, ManifestLocation::kInternal); | 
|  |  | 
|  | ASSERT_TRUE(extension.get()); | 
|  |  | 
|  | const WebAppLinkedShortcutItems& linked_shortcut_items = | 
|  | WebAppLinkedShortcutItems::GetWebAppLinkedShortcutItems(extension.get()); | 
|  | const std::map<int, ExtensionIconSet>& shortcut_icons = | 
|  | WebAppShortcutIconsInfo::GetShortcutIcons(extension.get()); | 
|  | for (size_t i = 0; i < web_app.shortcuts_menu_item_infos.size(); ++i) { | 
|  | const std::vector<WebApplicationShortcutsMenuItemInfo::Icon>& icon_infos = | 
|  | web_app.shortcuts_menu_item_infos[i].GetShortcutIconInfosForPurpose( | 
|  | IconPurpose::ANY); | 
|  | const std::vector<WebAppLinkedShortcutItems::ShortcutItemInfo::IconInfo>& | 
|  | linked_shortcut_icons_info = | 
|  | linked_shortcut_items.shortcut_item_infos[i] | 
|  | .shortcut_item_icon_infos; | 
|  | ASSERT_EQ(icon_infos.size(), linked_shortcut_icons_info.size()); | 
|  | for (size_t j = 0; j < icon_infos.size(); ++j) { | 
|  | EXPECT_EQ(linked_shortcut_icons_info[j].url, icon_infos[j].url); | 
|  | EXPECT_EQ(linked_shortcut_icons_info[j].size, | 
|  | icon_infos[j].square_size_px); | 
|  | } | 
|  |  | 
|  | const std::map<SquareSizePx, SkBitmap>& icon_bitmaps = | 
|  | web_app.shortcuts_menu_icon_bitmaps[i].any; | 
|  | EXPECT_EQ(icon_bitmaps.size(), shortcut_icons.at(i).map().size()); | 
|  | for (const std::pair<const SquareSizePx, SkBitmap>& icon : icon_bitmaps) { | 
|  | int size = icon.first; | 
|  | EXPECT_EQ( | 
|  | base::StringPrintf("shortcut_icons/%i/%i.png", static_cast<int>(i), | 
|  | size), | 
|  | shortcut_icons.at(i).Get(size, ExtensionIconSet::MATCH_EXACTLY)); | 
|  |  | 
|  | ExtensionResource resource = WebAppShortcutIconsInfo::GetIconResource( | 
|  | extension.get(), i, size, ExtensionIconSet::MATCH_EXACTLY); | 
|  | EXPECT_TRUE(base::PathExists(resource.GetFilePath())); | 
|  | ASSERT_TRUE(!resource.empty()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace extensions |