Commit 363a2a07 authored by Trent Apted's avatar Trent Apted Committed by Commit Bot

Register chrome://media-app as handling RAW image file types.

More accurately, this just hides the codepath that would redirect files
sniffed as "image/tiff" back to the old gallery app if the file had an
extension matching a known RAW image format. (chrome://media-app was
already a handler for "image/*", so logic was needed to exclude RAW).

However, it also adds explicit extension-match file handlers to the
web app manifest to handle cases where sniffing is not done. Initially
tried sharing the logic that drives `IsRawFile()` in the files app's
file_tasks but that's not really compatible with the web app manifest,
and `IsRawFile` can just be deleted soon. Encapsulate IsRawFile() to
make that removal easier: nothing outside file_tasks.cc uses it since
r777758.

The logic is put behind a base::Feature, but not a chrome://flag: usage
of this is low and the change is low-risk. And flipping
chrome://flags#media-app also works to use the old gallery for these
file types.

Bug: 1030935, b/154062029
Change-Id: I4779f76fa81800ffef316748cb94e12534b38775
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2494404Reviewed-by: default avatarLuciano Pacheco <lucmult@chromium.org>
Commit-Queue: Trent Apted <tapted@chromium.org>
Cr-Commit-Position: refs/heads/master@{#820645}
parent 2fc6005e
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
#include "chrome/common/extensions/api/file_manager_private.h" #include "chrome/common/extensions/api/file_manager_private.h"
#include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/pref_names.h" #include "chrome/common/pref_names.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/constants/chromeos_switches.h" #include "chromeos/constants/chromeos_switches.h"
#include "components/drive/drive_api_util.h" #include "components/drive/drive_api_util.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
...@@ -170,6 +171,20 @@ void RemoveFileManagerInternalActions(const std::set<std::string>& actions, ...@@ -170,6 +171,20 @@ void RemoveFileManagerInternalActions(const std::set<std::string>& actions,
tasks->swap(filtered); tasks->swap(filtered);
} }
// Returns whether |path| is a RAW image file according to its extension. Note
// that since none of the extensions of interest are "known" mime types (per
// net/mime_util.cc), it's enough to simply check the extension rather than
// using MimeTypeCollector. TODO(crbug/1030935): Remove this.
bool IsRawImage(const base::FilePath& path) {
constexpr const char* kRawExtensions[] = {".arw", ".cr2", ".dng", ".nef",
".nrw", ".orf", ".raf", ".rw2"};
for (const char* extension : kRawExtensions) {
if (path.MatchesExtension(extension))
return true;
}
return false;
}
// Adjusts |tasks| to reflect the product decision that chrome://media-app // Adjusts |tasks| to reflect the product decision that chrome://media-app
// should behave more like a user-installed app than a fallback handler. // should behave more like a user-installed app than a fallback handler.
// Specifically, only apps set as the default in user prefs should be preferred // Specifically, only apps set as the default in user prefs should be preferred
...@@ -192,13 +207,15 @@ void AdjustTasksForMediaApp(const std::vector<extensions::EntryInfo>& entries, ...@@ -192,13 +207,15 @@ void AdjustTasksForMediaApp(const std::vector<extensions::EntryInfo>& entries,
if (media_app_task == tasks->end()) if (media_app_task == tasks->end())
return; return;
// TODO(crbug/1030935): Once Media app supports RAW files, delete the // TODO(crbug/1030935): Delete the IsRawImage function and early exit when
// IsRawImage early exit. This is necessary while Gallery is still the // kMediaAppHandlesRaw is removed. The any_non_image check can be removed once
// better option for RAW files. The any_non_image check can be removed once // video player functionality of the Media App is fully polished
// video player functionality of the Media App is fully polished. // (b/171154148).
bool any_non_image = false; bool any_non_image = false;
for (const auto& entry : entries) { for (const auto& entry : entries) {
if (IsRawImage(entry.path)) { if (!base::FeatureList::IsEnabled(
chromeos::features::kMediaAppHandlesRaw) &&
IsRawImage(entry.path)) {
tasks->erase(media_app_task); tasks->erase(media_app_task);
return; // Let Gallery handle it. return; // Let Gallery handle it.
} }
...@@ -914,16 +931,6 @@ void ChooseAndSetDefaultTask(const PrefService& pref_service, ...@@ -914,16 +931,6 @@ void ChooseAndSetDefaultTask(const PrefService& pref_service,
} }
} }
bool IsRawImage(const base::FilePath& path) {
constexpr const char* kRawExtensions[] = {".arw", ".cr2", ".dng", ".nef",
".nrw", ".orf", ".raf", ".rw2"};
for (const char* extension : kRawExtensions) {
if (path.MatchesExtension(extension))
return true;
}
return false;
}
bool IsHtmlFile(const base::FilePath& path) { bool IsHtmlFile(const base::FilePath& path) {
constexpr const char* kHtmlExtensions[] = {".htm", ".html", ".mhtml", constexpr const char* kHtmlExtensions[] = {".htm", ".html", ".mhtml",
".xht", ".xhtm", ".xhtml"}; ".xht", ".xhtm", ".xhtml"};
......
...@@ -340,12 +340,6 @@ void ChooseAndSetDefaultTask(const PrefService& pref_service, ...@@ -340,12 +340,6 @@ void ChooseAndSetDefaultTask(const PrefService& pref_service,
const std::vector<extensions::EntryInfo>& entries, const std::vector<extensions::EntryInfo>& entries,
std::vector<FullTaskDescriptor>* tasks); std::vector<FullTaskDescriptor>* tasks);
// Returns whether |path| is a RAW image file according to its extension. Note
// that since none of the extensions of interest are "known" mime types (per
// net/mime_util.cc), it's enough to simply check the extension rather than
// using MimeTypeCollector. TODO(crbug/1030935): Remove this.
bool IsRawImage(const base::FilePath& path);
// Returns whether |path| is an HTML file according to its extension. // Returns whether |path| is an HTML file according to its extension.
bool IsHtmlFile(const base::FilePath& path); bool IsHtmlFile(const base::FilePath& path);
......
...@@ -126,13 +126,14 @@ class FileTasksBrowserTestBase ...@@ -126,13 +126,14 @@ class FileTasksBrowserTestBase
base::FilePath path = prefix.AddExtension(extension); base::FilePath path = prefix.AddExtension(extension);
std::string mime_type; std::string mime_type;
net::GetMimeTypeFromFile(path, &mime_type); net::GetMimeTypeFromFile(path, &mime_type);
if (test.mime_type) { if (test.mime_type != nullptr) {
// Sniffing isn't used when GetMimeTypeFromFile() succeeds, so there // Sniffing isn't used when GetMimeTypeFromFile() succeeds, so there
// shouldn't be a hard-coded mime type configured. // shouldn't be a hard-coded mime type configured.
EXPECT_TRUE(mime_type.empty()); EXPECT_TRUE(mime_type.empty());
mime_type = test.mime_type; mime_type = test.mime_type;
} else {
EXPECT_FALSE(mime_type.empty()) << "No mime type for " << path;
} }
EXPECT_FALSE(mime_type.empty()) << "No mime type for " << path;
entries.push_back({path, mime_type, false}); entries.push_back({path, mime_type, false});
} }
std::vector<GURL> file_urls{entries.size(), GURL()}; std::vector<GURL> file_urls{entries.size(), GURL()};
...@@ -160,8 +161,24 @@ class FileTasksBrowserTest : public FileTasksBrowserTestBase { ...@@ -160,8 +161,24 @@ class FileTasksBrowserTest : public FileTasksBrowserTestBase {
class FileTasksBrowserTestWithMediaApp : public FileTasksBrowserTestBase { class FileTasksBrowserTestWithMediaApp : public FileTasksBrowserTestBase {
public: public:
FileTasksBrowserTestWithMediaApp() { FileTasksBrowserTestWithMediaApp() {
// Enable Media App. // Enable Media App with Raw support.
scoped_feature_list_.InitWithFeatures({chromeos::features::kMediaApp}, {}); scoped_feature_list_.InitWithFeatures(
{chromeos::features::kMediaApp,
chromeos::features::kMediaAppHandlesRaw},
{});
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
class FileTasksBrowserTestWithMediaAppNoRaw : public FileTasksBrowserTestBase {
public:
FileTasksBrowserTestWithMediaAppNoRaw() {
// Enable Media App. Disable Raw support.
scoped_feature_list_.InitWithFeatures(
{chromeos::features::kMediaApp},
{chromeos::features::kMediaAppHandlesRaw});
} }
private: private:
...@@ -179,17 +196,6 @@ class FileTasksBrowserTestWithMediaApp : public FileTasksBrowserTestBase { ...@@ -179,17 +196,6 @@ class FileTasksBrowserTestWithMediaApp : public FileTasksBrowserTestBase {
// not operate on real files. We hard code MIME types that file sniffing // not operate on real files. We hard code MIME types that file sniffing
// obtained experimentally from sample files. // obtained experimentally from sample files.
constexpr Expectation kUnchangedExpectations[] = { constexpr Expectation kUnchangedExpectations[] = {
// Raw.
{"arw", kGalleryAppId, "image/tiff"},
{"cr2", kGalleryAppId, "image/tiff"},
{"dng", kGalleryAppId, "image/tiff"},
{"nef", kGalleryAppId, "image/tiff"},
{"nrw", kGalleryAppId, "image/tiff"},
{"orf", kGalleryAppId, "image/tiff"},
{"raf", kGalleryAppId, "image/tiff"},
{"rw2", kGalleryAppId, "image/tiff"},
{"NRW", kGalleryAppId, "image/tiff"}, // Uppercase extension.
// Video. // Video.
{"3gp", kVideoPlayerAppId, "application/octet-stream"}, {"3gp", kVideoPlayerAppId, "application/octet-stream"},
{"avi", kVideoPlayerAppId, "application/octet-stream"}, {"avi", kVideoPlayerAppId, "application/octet-stream"},
...@@ -293,9 +299,24 @@ IN_PROC_BROWSER_TEST_P(FileTasksBrowserTest, DefaultHandlerChangeDetector) { ...@@ -293,9 +299,24 @@ IN_PROC_BROWSER_TEST_P(FileTasksBrowserTest, DefaultHandlerChangeDetector) {
// With the Media App disabled, all images should be handled by Gallery. // With the Media App disabled, all images should be handled by Gallery.
std::vector<Expectation> expectations = { std::vector<Expectation> expectations = {
// Images. // Images.
{"bmp", kGalleryAppId}, {"gif", kGalleryAppId}, {"ico", kGalleryAppId}, {"bmp", kGalleryAppId},
{"jpg", kGalleryAppId}, {"jpeg", kGalleryAppId}, {"png", kGalleryAppId}, {"gif", kGalleryAppId},
{"ico", kGalleryAppId},
{"jpg", kGalleryAppId},
{"jpeg", kGalleryAppId},
{"png", kGalleryAppId},
{"webp", kGalleryAppId}, {"webp", kGalleryAppId},
// Raw.
{"arw", kGalleryAppId, "image/tiff"},
{"cr2", kGalleryAppId, "image/tiff"},
{"dng", kGalleryAppId, "image/tiff"},
{"nef", kGalleryAppId, "image/tiff"},
{"nrw", kGalleryAppId, "image/tiff"},
{"orf", kGalleryAppId, "image/tiff"},
{"raf", kGalleryAppId, "image/tiff"},
{"rw2", kGalleryAppId, "image/tiff"},
{"NRW", kGalleryAppId, "image/tiff"}, // Uppercase extension.
{"arw", kGalleryAppId, ""}, // Missing MIME type (unable to sniff).
}; };
expectations.insert(expectations.end(), std::begin(kUnchangedExpectations), expectations.insert(expectations.end(), std::begin(kUnchangedExpectations),
std::end(kUnchangedExpectations)); std::end(kUnchangedExpectations));
...@@ -321,9 +342,57 @@ IN_PROC_BROWSER_TEST_P(FileTasksBrowserTestWithMediaApp, ...@@ -321,9 +342,57 @@ IN_PROC_BROWSER_TEST_P(FileTasksBrowserTestWithMediaApp,
// video, which it also handles should be unchanged). // video, which it also handles should be unchanged).
std::vector<Expectation> expectations = { std::vector<Expectation> expectations = {
// Images. // Images.
{"bmp", kMediaAppId}, {"gif", kMediaAppId}, {"ico", kMediaAppId}, {"bmp", kMediaAppId},
{"jpg", kMediaAppId}, {"jpeg", kMediaAppId}, {"png", kMediaAppId}, {"gif", kMediaAppId},
{"ico", kMediaAppId},
{"jpg", kMediaAppId},
{"jpeg", kMediaAppId},
{"png", kMediaAppId},
{"webp", kMediaAppId},
// Raw (handled by MediaApp).
{"arw", kMediaAppId, "image/tiff"},
{"cr2", kMediaAppId, "image/tiff"},
{"dng", kMediaAppId, "image/tiff"},
{"nef", kMediaAppId, "image/tiff"},
{"nrw", kMediaAppId, "image/tiff"},
{"orf", kMediaAppId, "image/tiff"},
{"raf", kMediaAppId, "image/tiff"},
{"rw2", kMediaAppId, "image/tiff"},
{"NRW", kMediaAppId, "image/tiff"}, // Uppercase extension.
{"arw", kMediaAppId, ""}, // Missing MIME type (unable to sniff).
};
expectations.insert(expectations.end(), std::begin(kUnchangedExpectations),
std::end(kUnchangedExpectations));
TestExpectationsAgainstDefaultTasks(expectations);
}
// Tests the default handlers with the Media App installed, but RAW support
// disabled.
IN_PROC_BROWSER_TEST_P(FileTasksBrowserTestWithMediaAppNoRaw,
DefaultHandlerChangeDetector) {
// With the Media App enabled, images should be handled by it by default (but
// video, which it also handles should be unchanged).
std::vector<Expectation> expectations = {
// Images.
{"bmp", kMediaAppId},
{"gif", kMediaAppId},
{"ico", kMediaAppId},
{"jpg", kMediaAppId},
{"jpeg", kMediaAppId},
{"png", kMediaAppId},
{"webp", kMediaAppId}, {"webp", kMediaAppId},
// Raw (still handled by gallery).
{"arw", kGalleryAppId, "image/tiff"},
{"cr2", kGalleryAppId, "image/tiff"},
{"dng", kGalleryAppId, "image/tiff"},
{"nef", kGalleryAppId, "image/tiff"},
{"nrw", kGalleryAppId, "image/tiff"},
{"orf", kGalleryAppId, "image/tiff"},
{"raf", kGalleryAppId, "image/tiff"},
{"rw2", kGalleryAppId, "image/tiff"},
{"NRW", kGalleryAppId, "image/tiff"}, // Uppercase extension.
{"arw", kGalleryAppId, ""}, // Missing MIME type (unable to sniff).
}; };
expectations.insert(expectations.end(), std::begin(kUnchangedExpectations), expectations.insert(expectations.end(), std::begin(kUnchangedExpectations),
std::end(kUnchangedExpectations)); std::end(kUnchangedExpectations));
...@@ -460,5 +529,12 @@ INSTANTIATE_TEST_SUITE_P(All, ...@@ -460,5 +529,12 @@ INSTANTIATE_TEST_SUITE_P(All,
TestProfileType::kGuest), TestProfileType::kGuest),
TestProfileTypeToString); TestProfileTypeToString);
INSTANTIATE_TEST_SUITE_P(All,
FileTasksBrowserTestWithMediaAppNoRaw,
::testing::Values(TestProfileType::kRegular,
TestProfileType::kIncognito,
TestProfileType::kGuest),
TestProfileTypeToString);
} // namespace file_tasks } // namespace file_tasks
} // namespace file_manager } // namespace file_manager
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <memory> #include <memory>
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "chrome/browser/chromeos/web_applications/system_web_app_install_utils.h" #include "chrome/browser/chromeos/web_applications/system_web_app_install_utils.h"
#include "chrome/browser/web_applications/components/web_app_constants.h" #include "chrome/browser/web_applications/components/web_app_constants.h"
...@@ -17,6 +18,38 @@ ...@@ -17,6 +18,38 @@
#include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
namespace {
// FileHandler configuration.
// See https://github.com/WICG/file-handling/blob/master/explainer.md.
constexpr std::tuple<const char*, const char*> kFileHandlers[] = {
{"image/*", ""},
{"video/*", ""},
// Raw images. Note the MIME type doesn't really matter here. MIME sniffing
// logic in the files app tends to detect image/tiff, but sniffing is only
// done for "local" files, so the extension list is needed in addition to
// the "image/*" wildcard above. The MIME type is never sent to the web app.
{"image/tiff", ".arw,.cr2,.dng,.nef,.nrw,.orf,.raf,.rw2"},
};
using AcceptMap = decltype(blink::Manifest::FileHandler::accept);
// Converts the kFileHandlers constexpr into the std::map needed to populate the
// web app manifest's `accept` property.
AcceptMap MakeHandlerAccept() {
AcceptMap result;
const base::string16 separator = base::ASCIIToUTF16(",");
for (const auto& handler : kFileHandlers) {
result[base::ASCIIToUTF16(std::get<0>(handler))] =
base::SplitString(base::ASCIIToUTF16(std::get<1>(handler)), separator,
base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
}
return result;
}
} // namespace
std::unique_ptr<WebApplicationInfo> CreateWebAppInfoForMediaWebApp() { std::unique_ptr<WebApplicationInfo> CreateWebAppInfoForMediaWebApp() {
std::unique_ptr<WebApplicationInfo> info = std::unique_ptr<WebApplicationInfo> info =
std::make_unique<WebApplicationInfo>(); std::make_unique<WebApplicationInfo>();
...@@ -45,8 +78,7 @@ std::unique_ptr<WebApplicationInfo> CreateWebAppInfoForMediaWebApp() { ...@@ -45,8 +78,7 @@ std::unique_ptr<WebApplicationInfo> CreateWebAppInfoForMediaWebApp() {
blink::Manifest::FileHandler file_handler; blink::Manifest::FileHandler file_handler;
file_handler.action = GURL(chromeos::kChromeUIMediaAppURL); file_handler.action = GURL(chromeos::kChromeUIMediaAppURL);
file_handler.name = base::UTF8ToUTF16("Media File"); file_handler.name = base::UTF8ToUTF16("Media File");
file_handler.accept[base::UTF8ToUTF16("image/*")] = {}; file_handler.accept = MakeHandlerAccept();
file_handler.accept[base::UTF8ToUTF16("video/*")] = {}; info->file_handlers.push_back(std::move(file_handler));
info->file_handlers.push_back(file_handler);
return info; return info;
} }
...@@ -435,6 +435,11 @@ const base::Feature kMinimumChromeVersion{"MinimumChromeVersion", ...@@ -435,6 +435,11 @@ const base::Feature kMinimumChromeVersion{"MinimumChromeVersion",
// ChromeOS Media App. https://crbug.com/996088. // ChromeOS Media App. https://crbug.com/996088.
const base::Feature kMediaApp{"MediaApp", base::FEATURE_ENABLED_BY_DEFAULT}; const base::Feature kMediaApp{"MediaApp", base::FEATURE_ENABLED_BY_DEFAULT};
// Whether known extensions for RAW image formats are handled by the ChromeOS
// Media App.
const base::Feature kMediaAppHandlesRaw{"MediaAppHandlesRaw",
base::FEATURE_ENABLED_BY_DEFAULT};
// Enables a unique URL for each path in CrOS settings. // Enables a unique URL for each path in CrOS settings.
// This allows deep linking to individual settings, i.e. in settings search. // This allows deep linking to individual settings, i.e. in settings search.
const base::Feature kOsSettingsDeepLinking{"OsSettingsDeepLinking", const base::Feature kOsSettingsDeepLinking{"OsSettingsDeepLinking",
......
...@@ -196,6 +196,8 @@ COMPONENT_EXPORT(CHROMEOS_CONSTANTS) ...@@ -196,6 +196,8 @@ COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const base::Feature kLoginDisplayPasswordButton; extern const base::Feature kLoginDisplayPasswordButton;
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kMediaApp; COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kMediaApp;
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const base::Feature kMediaAppHandlesRaw;
COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const base::Feature kMinimumChromeVersion; extern const base::Feature kMinimumChromeVersion;
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const base::Feature kOsSettingsDeepLinking; extern const base::Feature kOsSettingsDeepLinking;
......
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