Commit cab2bf7d authored by Moe Ahmadi's avatar Moe Ahmadi Committed by Commit Bot

[AR][IOS] Adds support for .USDZ file extension for ARKit 3D models

Many static file hosting services do not allow setting the appropriate
content-type when serving files. This CL relaxes the requirement to display
USDZ 3D models with ARQuickLook by checking the file extension in addition
to the content type.

Bug: 958938
Change-Id: I1f1a216d3e4f8bbe660242f5acb2eadef0c57509
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1597121
Commit-Queue: Moe Ahmadi <mahmadi@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Cr-Commit-Position: refs/heads/master@{#657726}
parent 9e77a4c9
...@@ -35,7 +35,8 @@ namespace { ...@@ -35,7 +35,8 @@ namespace {
// Returns a suffix for Download.IOSDownloadARModelState histogram for the // Returns a suffix for Download.IOSDownloadARModelState histogram for the
// |download_task|. // |download_task|.
std::string GetMimeTypeSuffix(web::DownloadTask* download_task) { std::string GetMimeTypeSuffix(web::DownloadTask* download_task) {
DCHECK(IsUsdzFileFormat(download_task->GetOriginalMimeType())); DCHECK(IsUsdzFileFormat(download_task->GetOriginalMimeType(),
download_task->GetSuggestedFilename()));
return kUsdzMimeTypeHistogramSuffix; return kUsdzMimeTypeHistogramSuffix;
} }
...@@ -50,7 +51,8 @@ IOSDownloadARModelState GetHistogramEnum(web::DownloadTask* download_task) { ...@@ -50,7 +51,8 @@ IOSDownloadARModelState GetHistogramEnum(web::DownloadTask* download_task) {
return IOSDownloadARModelState::kStarted; return IOSDownloadARModelState::kStarted;
} }
DCHECK(download_task->IsDone()); DCHECK(download_task->IsDone());
if (!IsUsdzFileFormat(download_task->GetMimeType())) { if (!IsUsdzFileFormat(download_task->GetMimeType(),
download_task->GetSuggestedFilename())) {
return IOSDownloadARModelState::kWrongMimeTypeFailure; return IOSDownloadARModelState::kWrongMimeTypeFailure;
} }
if (download_task->GetHttpCode() == 401 || if (download_task->GetHttpCode() == 401 ||
...@@ -120,7 +122,8 @@ void ARQuickLookTabHelper::DidFinishDownload() { ...@@ -120,7 +122,8 @@ void ARQuickLookTabHelper::DidFinishDownload() {
// Inform the delegate only if the download has been successful. // Inform the delegate only if the download has been successful.
if (download_task_->GetHttpCode() == 401 || if (download_task_->GetHttpCode() == 401 ||
download_task_->GetHttpCode() == 403 || download_task_->GetErrorCode() || download_task_->GetHttpCode() == 403 || download_task_->GetErrorCode() ||
!IsUsdzFileFormat(download_task_->GetMimeType())) { !IsUsdzFileFormat(download_task_->GetMimeType(),
download_task_->GetSuggestedFilename())) {
return; return;
} }
......
...@@ -36,6 +36,7 @@ const char kHistogramName[] = "Download.IOSDownloadARModelState.USDZ"; ...@@ -36,6 +36,7 @@ const char kHistogramName[] = "Download.IOSDownloadARModelState.USDZ";
const char kUrl[] = "https://test.test/"; const char kUrl[] = "https://test.test/";
NSString* const kTestSuggestedFileName = @"important_file.zip"; NSString* const kTestSuggestedFileName = @"important_file.zip";
NSString* const kTestUsdzFileName = @"important_file.usdz";
} // namespace } // namespace
...@@ -64,8 +65,46 @@ class ARQuickLookTabHelperTest : public PlatformTest, ...@@ -64,8 +65,46 @@ class ARQuickLookTabHelperTest : public PlatformTest,
base::HistogramTester histogram_tester_; base::HistogramTester histogram_tester_;
}; };
// Tests successfully downloading a USDZ file. // Tests successfully downloading a USDZ file with the appropriate extension.
TEST_P(ARQuickLookTabHelperTest, Success) { TEST_F(ARQuickLookTabHelperTest, SuccessFileExtention) {
auto task = std::make_unique<web::FakeDownloadTask>(GURL(kUrl), "other");
task->SetSuggestedFilename(base::SysNSStringToUTF16(kTestUsdzFileName));
web::FakeDownloadTask* task_ptr = task.get();
tab_helper()->Download(std::move(task));
ASSERT_TRUE(WaitUntilConditionOrTimeout(kWaitForDownloadTimeout, ^{
base::RunLoop().RunUntilIdle();
return task_ptr->GetState() == web::DownloadTask::State::kInProgress;
}));
task_ptr->SetDone(true);
EXPECT_EQ(1U, delegate().fileURLs.count);
EXPECT_TRUE([delegate().fileURLs.firstObject isKindOfClass:[NSURL class]]);
// Downloaded file should be located in download directory.
base::FilePath file =
task_ptr->GetResponseWriter()->AsFileWriter()->file_path();
base::FilePath download_dir;
ASSERT_TRUE(GetDownloadsDirectory(&download_dir));
EXPECT_TRUE(download_dir.IsParent(file));
histogram_tester()->ExpectBucketCount(
kHistogramName,
static_cast<base::HistogramBase::Sample>(
IOSDownloadARModelState::kCreated),
1);
histogram_tester()->ExpectBucketCount(
kHistogramName,
static_cast<base::HistogramBase::Sample>(
IOSDownloadARModelState::kStarted),
1);
histogram_tester()->ExpectBucketCount(
kHistogramName,
static_cast<base::HistogramBase::Sample>(
IOSDownloadARModelState::kSuccessful),
1);
}
// Tests successfully downloading a USDZ file with the appropriate content-type.
TEST_P(ARQuickLookTabHelperTest, SuccessContentType) {
auto task = std::make_unique<web::FakeDownloadTask>(GURL(kUrl), GetParam()); auto task = std::make_unique<web::FakeDownloadTask>(GURL(kUrl), GetParam());
task->SetSuggestedFilename(base::SysNSStringToUTF16(kTestSuggestedFileName)); task->SetSuggestedFilename(base::SysNSStringToUTF16(kTestSuggestedFileName));
web::FakeDownloadTask* task_ptr = task.get(); web::FakeDownloadTask* task_ptr = task.get();
......
...@@ -111,7 +111,8 @@ void BrowserDownloadService::OnDownloadCreated( ...@@ -111,7 +111,8 @@ void BrowserDownloadService::OnDownloadCreated(
if (tab_helper) { if (tab_helper) {
tab_helper->Download(std::move(task)); tab_helper->Download(std::move(task));
} }
} else if (IsUsdzFileFormat(task->GetMimeType()) && } else if (IsUsdzFileFormat(task->GetMimeType(),
task->GetSuggestedFilename()) &&
download::IsUsdzPreviewEnabled()) { download::IsUsdzPreviewEnabled()) {
ARQuickLookTabHelper* tab_helper = ARQuickLookTabHelper* tab_helper =
ARQuickLookTabHelper::FromWebState(web_state); ARQuickLookTabHelper::FromWebState(web_state);
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <vector> #include <vector>
#include "base/macros.h" #include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h" #include "base/test/metrics/histogram_tester.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h" #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#import "ios/chrome/browser/download/ar_quick_look_tab_helper.h" #import "ios/chrome/browser/download/ar_quick_look_tab_helper.h"
...@@ -29,6 +30,7 @@ ...@@ -29,6 +30,7 @@
namespace { namespace {
char kUrl[] = "https://test.test/"; char kUrl[] = "https://test.test/";
char kUsdzFileName[] = "important_file.usdz";
// Substitutes real TabHelper for testing. // Substitutes real TabHelper for testing.
template <class TabHelper> template <class TabHelper>
...@@ -157,6 +159,30 @@ TEST_F(BrowserDownloadServiceTest, PkPassMimeType) { ...@@ -157,6 +159,30 @@ TEST_F(BrowserDownloadServiceTest, PkPassMimeType) {
1); 1);
} }
// Tests that BrowserDownloadService uses ARQuickLookTabHelper for .USDZ
// extension.
TEST_F(BrowserDownloadServiceTest, UsdzExtension) {
if (!download::IsUsdzPreviewEnabled()) {
// Disabled on iOS versions below 12 because QLPreviewController is not
// available.
return;
}
ASSERT_TRUE(download_controller()->GetDelegate());
auto task = std::make_unique<web::FakeDownloadTask>(GURL(kUrl), "other");
task->SetSuggestedFilename(base::UTF8ToUTF16(kUsdzFileName));
web::DownloadTask* task_ptr = task.get();
download_controller()->GetDelegate()->OnDownloadCreated(
download_controller(), &web_state_, std::move(task));
ASSERT_EQ(1U, ar_quick_look_tab_helper()->tasks().size());
EXPECT_EQ(task_ptr, ar_quick_look_tab_helper()->tasks()[0].get());
ASSERT_TRUE(download_manager_tab_helper()->tasks().empty());
histogram_tester_.ExpectUniqueSample(
"Download.IOSDownloadMimeType",
static_cast<base::HistogramBase::Sample>(DownloadMimeTypeResult::Other),
1);
}
// Tests that BrowserDownloadService uses ARQuickLookTabHelper for USDZ Mime // Tests that BrowserDownloadService uses ARQuickLookTabHelper for USDZ Mime
// type. // type.
TEST_F(BrowserDownloadServiceTest, UsdzMimeType) { TEST_F(BrowserDownloadServiceTest, UsdzMimeType) {
......
...@@ -4,11 +4,18 @@ ...@@ -4,11 +4,18 @@
#include "ios/chrome/browser/download/usdz_mime_type.h" #include "ios/chrome/browser/download/usdz_mime_type.h"
#include "base/files/file_path.h"
#include "base/strings/utf_string_conversions.h"
char kUsdzFileExntension[] = ".usdz";
char kUsdzMimeType[] = "model/vnd.usdz+zip"; char kUsdzMimeType[] = "model/vnd.usdz+zip";
char kLegacyUsdzMimeType[] = "model/usd"; char kLegacyUsdzMimeType[] = "model/usd";
char kLegacyPixarUsdzMimeType[] = "model/vnd.pixar.usd"; char kLegacyPixarUsdzMimeType[] = "model/vnd.pixar.usd";
bool IsUsdzFileFormat(const std::string& mime_type) { bool IsUsdzFileFormat(const std::string& mime_type,
const base::string16& suggested_filename) {
return mime_type == kUsdzMimeType || mime_type == kLegacyUsdzMimeType || return mime_type == kUsdzMimeType || mime_type == kLegacyUsdzMimeType ||
mime_type == kLegacyPixarUsdzMimeType; mime_type == kLegacyPixarUsdzMimeType ||
base::FilePath(base::UTF16ToUTF8(suggested_filename))
.MatchesExtension(kUsdzFileExntension);
} }
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#include <string> #include <string>
#include "base/strings/string16.h"
// Universal Scene Description file format used to represent 3D models. // Universal Scene Description file format used to represent 3D models.
// See https://www.iana.org/assignments/media-types/model/vnd.usdz+zip // See https://www.iana.org/assignments/media-types/model/vnd.usdz+zip
extern char kUsdzMimeType[]; extern char kUsdzMimeType[];
...@@ -14,6 +16,10 @@ extern char kUsdzMimeType[]; ...@@ -14,6 +16,10 @@ extern char kUsdzMimeType[];
extern char kLegacyUsdzMimeType[]; extern char kLegacyUsdzMimeType[];
extern char kLegacyPixarUsdzMimeType[]; extern char kLegacyPixarUsdzMimeType[];
bool IsUsdzFileFormat(const std::string& mime_type); // Returns whether the content-type or the file extension match those of a USDZ
// 3D model. The file extension is checked in addition to the content-type since
// many static file hosting services do not allow setting the content-type.
bool IsUsdzFileFormat(const std::string& mime_type,
const base::string16& suggested_filename);
#endif // IOS_CHROME_BROWSER_DOWNLOAD_USDZ_MIME_TYPE_H_ #endif // IOS_CHROME_BROWSER_DOWNLOAD_USDZ_MIME_TYPE_H_
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