14e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
51320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "storage/browser/fileapi/file_system_context.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/scoped_temp_dir.h"
8ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "base/message_loop/message_loop.h"
95e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/stringprintf.h"
10c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch#include "content/browser/quota/mock_quota_manager.h"
115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "content/public/test/mock_special_storage_policy.h"
124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "content/public/test/test_file_system_options.h"
131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "storage/browser/fileapi/external_mount_points.h"
141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "storage/browser/fileapi/file_system_backend.h"
151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "storage/browser/fileapi/isolated_context.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "testing/gtest/include/gtest/gtest.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#define FPL(x) FILE_PATH_LITERAL(x)
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#if defined(FILE_PATH_USES_DRIVE_LETTERS)
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#define DRIVE FPL("C:")
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#else
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#define DRIVE
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#endif
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)using storage::ExternalMountPoints;
2703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)using storage::FileSystemBackend;
2803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)using storage::FileSystemContext;
2903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)using storage::FileSystemMountOption;
3003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)using storage::FileSystemURL;
3103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)using storage::IsolatedContext;
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace content {
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const char kTestOrigin[] = "http://chromium.org/";
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)GURL CreateRawFileSystemURL(const std::string& type_str,
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                            const std::string& fs_id) {
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string url_str = base::StringPrintf(
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      "filesystem:http://chromium.org/%s/%s/root/file",
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      type_str.c_str(),
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      fs_id.c_str());
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return GURL(url_str);
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class FileSystemContextTest : public testing::Test {
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public:
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FileSystemContextTest() {}
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void SetUp() {
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    storage_policy_ = new MockSpecialStoragePolicy();
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
57868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    mock_quota_manager_ =
58c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        new MockQuotaManager(false /* is_incognito */,
59868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                                    data_dir_.path(),
607d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                                    base::MessageLoopProxy::current().get(),
617d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                                    base::MessageLoopProxy::current().get(),
62868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)                                    storage_policy_.get());
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) protected:
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FileSystemContext* CreateFileSystemContextForTest(
6703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      storage::ExternalMountPoints* external_mount_points) {
6823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    return new FileSystemContext(
6923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        base::MessageLoopProxy::current().get(),
7023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        base::MessageLoopProxy::current().get(),
7123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        external_mount_points,
7223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        storage_policy_.get(),
7323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        mock_quota_manager_->proxy(),
7423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        ScopedVector<FileSystemBackend>(),
7503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        std::vector<storage::URLRequestAutoMountHandler>(),
7623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        data_dir_.path(),
7723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        CreateAllowFileAccessOptions());
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Verifies a *valid* filesystem url has expected values.
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void ExpectFileSystemURLMatches(const FileSystemURL& url,
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                  const GURL& expect_origin,
8303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                                  storage::FileSystemType expect_mount_type,
8403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                                  storage::FileSystemType expect_type,
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                  const base::FilePath& expect_path,
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                  const base::FilePath& expect_virtual_path,
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                  const std::string& expect_filesystem_id) {
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    EXPECT_TRUE(url.is_valid());
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    EXPECT_EQ(expect_origin, url.origin());
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    EXPECT_EQ(expect_mount_type, url.mount_type());
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    EXPECT_EQ(expect_type, url.type());
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    EXPECT_EQ(expect_path, url.path());
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    EXPECT_EQ(expect_virtual_path, url.virtual_path());
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    EXPECT_EQ(expect_filesystem_id, url.filesystem_id());
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private:
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::ScopedTempDir data_dir_;
100b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  base::MessageLoop message_loop_;
10103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  scoped_refptr<storage::SpecialStoragePolicy> storage_policy_;
102c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  scoped_refptr<MockQuotaManager> mock_quota_manager_;
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// It is not valid to pass NULL ExternalMountPoints to FileSystemContext on
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// ChromeOS.
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#if !defined(OS_CHROMEOS)
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(FileSystemContextTest, NullExternalMountPoints) {
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_refptr<FileSystemContext> file_system_context(
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      CreateFileSystemContextForTest(NULL));
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Cracking system external mount and isolated mount points should work.
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string isolated_name = "root";
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string isolated_id =
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      IsolatedContext::GetInstance()->RegisterFileSystemForPath(
11603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          storage::kFileSystemTypeNativeLocal,
1176d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)          std::string(),
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          base::FilePath(DRIVE FPL("/test/isolated/root")),
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          &isolated_name);
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Register system external mount point.
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      "system",
12303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      storage::kFileSystemTypeNativeLocal,
124a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      FileSystemMountOption(),
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::FilePath(DRIVE FPL("/test/sys/"))));
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FileSystemURL cracked_isolated = file_system_context->CrackURL(
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      CreateRawFileSystemURL("isolated", isolated_id));
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExpectFileSystemURLMatches(
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      cracked_isolated,
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      GURL(kTestOrigin),
13303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      storage::kFileSystemTypeIsolated,
13403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      storage::kFileSystemTypeNativeLocal,
13503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      base::FilePath(DRIVE FPL("/test/isolated/root/file"))
13603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          .NormalizePathSeparators(),
13703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      base::FilePath::FromUTF8Unsafe(isolated_id)
13803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          .Append(FPL("root/file"))
13903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          .NormalizePathSeparators(),
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      isolated_id);
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FileSystemURL cracked_external = file_system_context->CrackURL(
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      CreateRawFileSystemURL("external", "system"));
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExpectFileSystemURLMatches(
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      cracked_external,
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      GURL(kTestOrigin),
14803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      storage::kFileSystemTypeExternal,
14903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      storage::kFileSystemTypeNativeLocal,
15003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      base::FilePath(DRIVE FPL("/test/sys/root/file"))
15103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          .NormalizePathSeparators(),
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::FilePath(FPL("system/root/file")).NormalizePathSeparators(),
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      "system");
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  IsolatedContext::GetInstance()->RevokeFileSystem(isolated_id);
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExternalMountPoints::GetSystemInstance()->RevokeFileSystem("system");
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#endif  // !defiend(OS_CHROMEOS)
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(FileSystemContextTest, FileSystemContextKeepsMountPointsAlive) {
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_refptr<ExternalMountPoints> mount_points =
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ExternalMountPoints::CreateRefCounted();
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Register system external mount point.
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ASSERT_TRUE(mount_points->RegisterFileSystem(
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      "system",
16703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      storage::kFileSystemTypeNativeLocal,
168a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      FileSystemMountOption(),
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::FilePath(DRIVE FPL("/test/sys/"))));
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_refptr<FileSystemContext> file_system_context(
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      CreateFileSystemContextForTest(mount_points.get()));
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Release a MountPoints reference created in the test.
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  mount_points = NULL;
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // FileSystemContext should keep a reference to the |mount_points|, so it
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // should be able to resolve the URL.
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  FileSystemURL cracked_external = file_system_context->CrackURL(
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      CreateRawFileSystemURL("external", "system"));
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExpectFileSystemURLMatches(
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      cracked_external,
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      GURL(kTestOrigin),
18503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      storage::kFileSystemTypeExternal,
18603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      storage::kFileSystemTypeNativeLocal,
18703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      base::FilePath(DRIVE FPL("/test/sys/root/file"))
18803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          .NormalizePathSeparators(),
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::FilePath(FPL("system/root/file")).NormalizePathSeparators(),
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      "system");
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // No need to revoke the registered filesystem since |mount_points| lifetime
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // is bound to this test.
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TEST_F(FileSystemContextTest, CrackFileSystemURL) {
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_refptr<ExternalMountPoints> external_mount_points(
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ExternalMountPoints::CreateRefCounted());
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_refptr<FileSystemContext> file_system_context(
200868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      CreateFileSystemContextForTest(external_mount_points.get()));
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Register an isolated mount point.
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string isolated_file_system_name = "root";
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const std::string kIsolatedFileSystemID =
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      IsolatedContext::GetInstance()->RegisterFileSystemForPath(
20603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          storage::kFileSystemTypeNativeLocal,
2076d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)          std::string(),
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          base::FilePath(DRIVE FPL("/test/isolated/root")),
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          &isolated_file_system_name);
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Register system external mount point.
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      "system",
21303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      storage::kFileSystemTypeDrive,
214a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      FileSystemMountOption(),
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::FilePath(DRIVE FPL("/test/sys/"))));
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      "ext",
21803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      storage::kFileSystemTypeNativeLocal,
219a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      FileSystemMountOption(),
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::FilePath(DRIVE FPL("/test/ext"))));
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Register a system external mount point with the same name/id as the
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // registered isolated mount point.
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      kIsolatedFileSystemID,
22503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      storage::kFileSystemTypeRestrictedNativeLocal,
226a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      FileSystemMountOption(),
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::FilePath(DRIVE FPL("/test/system/isolated"))));
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Add a mount points with the same name as a system mount point to
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // FileSystemContext's external mount points.
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ASSERT_TRUE(external_mount_points->RegisterFileSystem(
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      "ext",
23203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      storage::kFileSystemTypeNativeLocal,
23303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      FileSystemMountOption(),
23403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      base::FilePath(DRIVE FPL("/test/local/ext/"))));
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const GURL kTestOrigin = GURL("http://chromium.org/");
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const base::FilePath kVirtualPathNoRoot = base::FilePath(FPL("root/file"));
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  struct TestCase {
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Test case values.
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::string root;
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::string type_str;
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Expected test results.
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    bool expect_is_valid;
24603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    storage::FileSystemType expect_mount_type;
24703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    storage::FileSystemType expect_type;
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath::CharType* expect_path;
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::string expect_filesystem_id;
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  };
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const TestCase kTestCases[] = {
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Following should not be handled by the url crackers:
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      {
25503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)       "pers_mount", "persistent", true /* is_valid */,
25603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)       storage::kFileSystemTypePersistent, storage::kFileSystemTypePersistent,
25703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)       FPL("pers_mount/root/file"), std::string() /* filesystem id */
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      },
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      {
26003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)       "temp_mount", "temporary", true /* is_valid */,
26103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)       storage::kFileSystemTypeTemporary, storage::kFileSystemTypeTemporary,
26203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)       FPL("temp_mount/root/file"), std::string() /* filesystem id */
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      },
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Should be cracked by isolated mount points:
26503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      {kIsolatedFileSystemID, "isolated", true /* is_valid */,
26603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)       storage::kFileSystemTypeIsolated, storage::kFileSystemTypeNativeLocal,
26703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)       DRIVE FPL("/test/isolated/root/file"), kIsolatedFileSystemID},
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Should be cracked by system mount points:
26903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      {"system", "external", true /* is_valid */,
27003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)       storage::kFileSystemTypeExternal, storage::kFileSystemTypeDrive,
27103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)       DRIVE FPL("/test/sys/root/file"), "system"},
27203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      {kIsolatedFileSystemID, "external", true /* is_valid */,
27303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)       storage::kFileSystemTypeExternal,
27403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)       storage::kFileSystemTypeRestrictedNativeLocal,
27503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)       DRIVE FPL("/test/system/isolated/root/file"), kIsolatedFileSystemID},
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Should be cracked by FileSystemContext's ExternalMountPoints.
27703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      {"ext", "external", true /* is_valid */, storage::kFileSystemTypeExternal,
27803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)       storage::kFileSystemTypeNativeLocal,
27903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)       DRIVE FPL("/test/local/ext/root/file"), "ext"},
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Test for invalid filesystem url (made invalid by adding invalid
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // filesystem type).
28203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      {"sytem", "external", false /* is_valid */,
28303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)       // The rest of values will be ignored.
28403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)       storage::kFileSystemTypeUnknown, storage::kFileSystemTypeUnknown,
28503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)       FPL(""), std::string()},
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // Test for URL with non-existing filesystem id.
28703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      {"invalid", "external", false /* is_valid */,
28803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)       // The rest of values will be ignored.
28903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)       storage::kFileSystemTypeUnknown, storage::kFileSystemTypeUnknown,
29003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)       FPL(""), std::string()},
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  };
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath virtual_path =
2954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        base::FilePath::FromUTF8Unsafe(
2964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)            kTestCases[i].root).Append(kVirtualPathNoRoot);
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    GURL raw_url =
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        CreateRawFileSystemURL(kTestCases[i].type_str, kTestCases[i].root);
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    FileSystemURL cracked_url = file_system_context->CrackURL(raw_url);
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SCOPED_TRACE(testing::Message() << "Test case " << i << ": "
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                    << "Cracking URL: " << raw_url);
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    EXPECT_EQ(kTestCases[i].expect_is_valid, cracked_url.is_valid());
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!kTestCases[i].expect_is_valid)
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ExpectFileSystemURLMatches(
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        cracked_url,
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        GURL(kTestOrigin),
3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        kTestCases[i].expect_mount_type,
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        kTestCases[i].expect_type,
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::FilePath(kTestCases[i].expect_path).NormalizePathSeparators(),
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        virtual_path.NormalizePathSeparators(),
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        kTestCases[i].expect_filesystem_id);
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  IsolatedContext::GetInstance()->RevokeFileSystemByPath(
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::FilePath(DRIVE FPL("/test/isolated/root")));
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExternalMountPoints::GetSystemInstance()->RevokeFileSystem("system");
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExternalMountPoints::GetSystemInstance()->RevokeFileSystem("ext");
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      kIsolatedFileSystemID);
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3271e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)TEST_F(FileSystemContextTest, CanServeURLRequest) {
3281e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  scoped_refptr<ExternalMountPoints> external_mount_points(
3291e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      ExternalMountPoints::CreateRefCounted());
3301e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  scoped_refptr<FileSystemContext> context(
3311e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      CreateFileSystemContextForTest(external_mount_points.get()));
3321e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
3331e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // A request for a sandbox mount point should be served.
3341e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  FileSystemURL cracked_url =
3351e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      context->CrackURL(CreateRawFileSystemURL("persistent", "pers_mount"));
33603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  EXPECT_EQ(storage::kFileSystemTypePersistent, cracked_url.mount_type());
3371e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  EXPECT_TRUE(context->CanServeURLRequest(cracked_url));
3381e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
3391e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // A request for an isolated mount point should NOT be served.
3401e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  std::string isolated_fs_name = "root";
3411e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  std::string isolated_fs_id =
3421e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      IsolatedContext::GetInstance()->RegisterFileSystemForPath(
34303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          storage::kFileSystemTypeNativeLocal,
3446d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)          std::string(),
3451e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          base::FilePath(DRIVE FPL("/test/isolated/root")),
3461e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          &isolated_fs_name);
3471e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  cracked_url = context->CrackURL(
3481e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      CreateRawFileSystemURL("isolated", isolated_fs_id));
34903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  EXPECT_EQ(storage::kFileSystemTypeIsolated, cracked_url.mount_type());
3501e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  EXPECT_FALSE(context->CanServeURLRequest(cracked_url));
3511e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
3521e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // A request for an external mount point should be served.
3531e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  const std::string kExternalMountName = "ext_mount";
3541e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  ASSERT_TRUE(ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
35503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      kExternalMountName,
35603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      storage::kFileSystemTypeDrive,
3575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      FileSystemMountOption(),
358a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      base::FilePath()));
3591e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  cracked_url = context->CrackURL(
3601e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      CreateRawFileSystemURL("external", kExternalMountName));
36103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  EXPECT_EQ(storage::kFileSystemTypeExternal, cracked_url.mount_type());
3621e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  EXPECT_TRUE(context->CanServeURLRequest(cracked_url));
3631e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
3641e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(
3651e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      kExternalMountName);
3661e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  IsolatedContext::GetInstance()->RevokeFileSystem(isolated_fs_id);
3671e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)}
3681e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
3692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace content
372