Cherry pick Add support for loading pak files from arbitrary file regions

Cherry pick of Chromium
https://src.chromium.org/viewvc/chrome?view=rev&revision=285945

conflict:
  ui/base/resource/resource_bundle.h

Add support for loading pak files from arbitrary file regions.

This is to support a new use case for Android: mmap a pak file
directly from the APK (where it will be stored uncompressed)
without extracting it first. This would save both precious space
on the flash and startup time on the first run.
This CL introduces:
 - the necessary changes to base::File to memory map arbitrary
   regions of a file.
 - The corresponding changes (plus unittests)  in DataPack and
   ResourceBundle to take advantage of the new support.
   At present state, this CL is not intended to introduce any
   behavioral change.

BUG=394502
Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=285945.

Bug: 16723226
Change-Id: I0ab9588e1c532ee339824dfabf89a73c316cf079
diff --git a/base/base.gyp b/base/base.gyp
index 5544a4b..c3076bf 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -464,6 +464,7 @@
         'files/file_unittest.cc',
         'files/file_util_proxy_unittest.cc',
         'files/important_file_writer_unittest.cc',
+        'files/memory_mapped_file_unittest.cc',
         'files/scoped_temp_dir_unittest.cc',
         'gmock_unittest.cc',
         'guid_unittest.cc',
diff --git a/base/files/memory_mapped_file.cc b/base/files/memory_mapped_file.cc
index ace4e11..e965649 100644
--- a/base/files/memory_mapped_file.cc
+++ b/base/files/memory_mapped_file.cc
@@ -6,9 +6,26 @@
 
 #include "base/files/file_path.h"
 #include "base/logging.h"
+#include "base/sys_info.h"
 
 namespace base {
 
+const MemoryMappedFile::Region MemoryMappedFile::Region::kWholeFile;
+
+MemoryMappedFile::Region::Region() : offset(0), size(0) {
+}
+
+MemoryMappedFile::Region::Region(int64 offset, int64 size)
+    : offset(offset), size(size) {
+  DCHECK_GE(offset, 0);
+  DCHECK_GT(size, 0);
+}
+
+bool MemoryMappedFile::Region::operator==(
+    const MemoryMappedFile::Region& other) const {
+  return other.offset == offset && other.size == size;
+}
+
 MemoryMappedFile::~MemoryMappedFile() {
   CloseHandles();
 }
@@ -24,7 +41,7 @@
     return false;
   }
 
-  if (!MapFileToMemory()) {
+  if (!MapFileRegionToMemory(Region::kWholeFile)) {
     CloseHandles();
     return false;
   }
@@ -33,12 +50,16 @@
 }
 
 bool MemoryMappedFile::Initialize(File file) {
+  return Initialize(file.Pass(), Region::kWholeFile);
+}
+
+bool MemoryMappedFile::Initialize(File file, const Region& region) {
   if (IsValid())
     return false;
 
   file_ = file.Pass();
 
-  if (!MapFileToMemory()) {
+  if (!MapFileRegionToMemory(region)) {
     CloseHandles();
     return false;
   }
@@ -50,4 +71,18 @@
   return data_ != NULL;
 }
 
+// static
+void MemoryMappedFile::CalculateVMAlignedBoundaries(int64 start,
+                                                    int64 size,
+                                                    int64* aligned_start,
+                                                    int64* aligned_size,
+                                                    int32* offset) {
+  // Sadly, on Windows, the mmap alignment is not just equal to the page size.
+  const int64 mask = static_cast<int64>(SysInfo::VMAllocationGranularity()) - 1;
+  DCHECK_LT(mask, std::numeric_limits<int32>::max());
+  *offset = start & mask;
+  *aligned_start = start & ~mask;
+  *aligned_size = (size + *offset + mask) & ~mask;
+}
+
 }  // namespace base
diff --git a/base/files/memory_mapped_file.h b/base/files/memory_mapped_file.h
index b02d8cf..dc56806 100644
--- a/base/files/memory_mapped_file.h
+++ b/base/files/memory_mapped_file.h
@@ -24,6 +24,26 @@
   MemoryMappedFile();
   ~MemoryMappedFile();
 
+  // Used to hold information about a region [offset + size] of a file.
+  struct BASE_EXPORT Region {
+    static const Region kWholeFile;
+
+    Region(int64 offset, int64 size);
+
+    bool operator==(const Region& other) const;
+
+    // Start of the region (measured in bytes from the beginning of the file).
+    int64 offset;
+
+    // Length of the region in bytes.
+    int64 size;
+
+   private:
+    // This ctor is used only by kWholeFile, to construct a zero-sized Region
+    // (which is forbidden by the public ctor) and uniquely identify kWholeFile.
+    Region();
+  };
+
   // Opens an existing file and maps it into memory. Access is restricted to
   // read only. If this object already points to a valid memory mapped file
   // then this method will fail and return false. If it cannot open the file,
@@ -35,6 +55,9 @@
   // ownership of |file| and closes it when done.
   bool Initialize(File file);
 
+  // As above, but works with a region of an already-opened file.
+  bool Initialize(File file, const Region& region);
+
 #if defined(OS_WIN)
   // Opens an existing file and maps it as an image section. Please refer to
   // the Initialize function above for additional information.
@@ -48,9 +71,21 @@
   bool IsValid() const;
 
  private:
+  // Given the arbitrarily aligned memory region [start, size], returns the
+  // boundaries of the region aligned to the granularity specified by the OS,
+  // (a page on Linux, ~32k on Windows) as follows:
+  // - |aligned_start| is page aligned and <= |start|.
+  // - |aligned_size| is a multiple of the VM granularity and >= |size|.
+  // - |offset| is the displacement of |start| w.r.t |aligned_start|.
+  static void CalculateVMAlignedBoundaries(int64 start,
+                                           int64 size,
+                                           int64* aligned_start,
+                                           int64* aligned_size,
+                                           int32* offset);
+
   // Map the file to memory, set data_ to that memory address. Return true on
   // success, false on any kind of failure. This is a helper for Initialize().
-  bool MapFileToMemory();
+  bool MapFileRegionToMemory(const Region& region);
 
   // Closes all open handles.
   void CloseHandles();
diff --git a/base/files/memory_mapped_file_posix.cc b/base/files/memory_mapped_file_posix.cc
index 5d7e007..ebf3877 100644
--- a/base/files/memory_mapped_file_posix.cc
+++ b/base/files/memory_mapped_file_posix.cc
@@ -16,22 +16,63 @@
 MemoryMappedFile::MemoryMappedFile() : data_(NULL), length_(0) {
 }
 
-bool MemoryMappedFile::MapFileToMemory() {
+bool MemoryMappedFile::MapFileRegionToMemory(
+    const MemoryMappedFile::Region& region) {
   ThreadRestrictions::AssertIOAllowed();
 
-  struct stat file_stat;
-  if (fstat(file_.GetPlatformFile(), &file_stat) == -1 ) {
-    DPLOG(ERROR) << "fstat " << file_.GetPlatformFile();
+  off_t map_start = 0;
+  size_t map_size = 0;
+  int32 data_offset = 0;
+
+  if (region == MemoryMappedFile::Region::kWholeFile) {
+    int64 file_len = file_.GetLength();
+    if (file_len == -1) {
+      DPLOG(ERROR) << "fstat " << file_.GetPlatformFile();
+      return false;
+    }
+    map_size = static_cast<size_t>(file_len);
+    length_ = map_size;
+  } else {
+    // The region can be arbitrarily aligned. mmap, instead, requires both the
+    // start and size to be page-aligned. Hence, we map here the page-aligned
+    // outer region [|aligned_start|, |aligned_start| + |size|] which contains
+    // |region| and then add up the |data_offset| displacement.
+    int64 aligned_start = 0;
+    int64 aligned_size = 0;
+    CalculateVMAlignedBoundaries(region.offset,
+                                 region.size,
+                                 &aligned_start,
+                                 &aligned_size,
+                                 &data_offset);
+
+    // Ensure that the casts in the mmap call below are sane.
+    if (aligned_start < 0 || aligned_size < 0 ||
+        aligned_start > std::numeric_limits<off_t>::max() ||
+        static_cast<uint64>(aligned_size) >
+            std::numeric_limits<size_t>::max() ||
+        static_cast<uint64>(region.size) > std::numeric_limits<size_t>::max()) {
+      DLOG(ERROR) << "Region bounds are not valid for mmap";
+      return false;
+    }
+
+    map_start = static_cast<off_t>(aligned_start);
+    map_size = static_cast<size_t>(aligned_size);
+    length_ = static_cast<size_t>(region.size);
+  }
+
+  data_ = static_cast<uint8*>(mmap(NULL,
+                                   map_size,
+                                   PROT_READ,
+                                   MAP_SHARED,
+                                   file_.GetPlatformFile(),
+                                   map_start));
+  if (data_ == MAP_FAILED) {
+    DPLOG(ERROR) << "mmap " << file_.GetPlatformFile();
     return false;
   }
-  length_ = file_stat.st_size;
 
-  data_ = static_cast<uint8*>(
-      mmap(NULL, length_, PROT_READ, MAP_SHARED, file_.GetPlatformFile(), 0));
-  if (data_ == MAP_FAILED)
-    DPLOG(ERROR) << "mmap " << file_.GetPlatformFile();
-
-  return data_ != MAP_FAILED;
+  data_ += data_offset;
+  return true;
 }
 
 void MemoryMappedFile::CloseHandles() {
diff --git a/base/files/memory_mapped_file_unittest.cc b/base/files/memory_mapped_file_unittest.cc
new file mode 100644
index 0000000..21b0df4
--- /dev/null
+++ b/base/files/memory_mapped_file_unittest.cc
@@ -0,0 +1,166 @@
+// Copyright 2014 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 "base/files/memory_mapped_file.h"
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace base {
+
+namespace {
+
+// Create a temporary buffer and fill it with a watermark sequence.
+scoped_ptr<uint8[]> CreateTestBuffer(size_t size, size_t offset) {
+  scoped_ptr<uint8[]> buf(new uint8[size]);
+  for (size_t i = 0; i < size; ++i)
+    buf.get()[i] = static_cast<uint8>((offset + i) % 253);
+  return buf.Pass();
+}
+
+// Check that the watermark sequence is consistent with the |offset| provided.
+bool CheckBufferContents(const uint8* data, size_t size, size_t offset) {
+  scoped_ptr<uint8[]> test_data(CreateTestBuffer(size, offset));
+  return memcmp(test_data.get(), data, size) == 0;
+}
+
+class MemoryMappedFileTest : public PlatformTest {
+ protected:
+  virtual void SetUp() OVERRIDE {
+    PlatformTest::SetUp();
+    CreateTemporaryFile(&temp_file_path_);
+  }
+
+  virtual void TearDown() { EXPECT_TRUE(DeleteFile(temp_file_path_, false)); }
+
+  void CreateTemporaryTestFile(size_t size) {
+    File file(temp_file_path_,
+              File::FLAG_CREATE_ALWAYS | File::FLAG_READ | File::FLAG_WRITE);
+    EXPECT_TRUE(file.IsValid());
+
+    scoped_ptr<uint8[]> test_data(CreateTestBuffer(size, 0));
+    size_t bytes_written =
+        file.Write(0, reinterpret_cast<char*>(test_data.get()), size);
+    EXPECT_EQ(size, bytes_written);
+    file.Close();
+  }
+
+  const FilePath temp_file_path() const { return temp_file_path_; }
+
+ private:
+  FilePath temp_file_path_;
+};
+
+TEST_F(MemoryMappedFileTest, MapWholeFileByPath) {
+  const size_t kFileSize = 68 * 1024;
+  CreateTemporaryTestFile(kFileSize);
+  MemoryMappedFile map;
+  map.Initialize(temp_file_path());
+  ASSERT_EQ(kFileSize, map.length());
+  ASSERT_TRUE(map.data() != NULL);
+  EXPECT_TRUE(map.IsValid());
+  ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0));
+}
+
+TEST_F(MemoryMappedFileTest, MapWholeFileByFD) {
+  const size_t kFileSize = 68 * 1024;
+  CreateTemporaryTestFile(kFileSize);
+  MemoryMappedFile map;
+  map.Initialize(File(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ));
+  ASSERT_EQ(kFileSize, map.length());
+  ASSERT_TRUE(map.data() != NULL);
+  EXPECT_TRUE(map.IsValid());
+  ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0));
+}
+
+TEST_F(MemoryMappedFileTest, MapSmallFile) {
+  const size_t kFileSize = 127;
+  CreateTemporaryTestFile(kFileSize);
+  MemoryMappedFile map;
+  map.Initialize(temp_file_path());
+  ASSERT_EQ(kFileSize, map.length());
+  ASSERT_TRUE(map.data() != NULL);
+  EXPECT_TRUE(map.IsValid());
+  ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0));
+}
+
+TEST_F(MemoryMappedFileTest, MapWholeFileUsingRegion) {
+  const size_t kFileSize = 157 * 1024;
+  CreateTemporaryTestFile(kFileSize);
+  MemoryMappedFile map;
+
+  File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
+  map.Initialize(file.Pass(), MemoryMappedFile::Region::kWholeFile);
+  ASSERT_EQ(kFileSize, map.length());
+  ASSERT_TRUE(map.data() != NULL);
+  EXPECT_TRUE(map.IsValid());
+  ASSERT_TRUE(CheckBufferContents(map.data(), kFileSize, 0));
+}
+
+TEST_F(MemoryMappedFileTest, MapPartialRegionAtBeginning) {
+  const size_t kFileSize = 157 * 1024;
+  const size_t kPartialSize = 4 * 1024 + 32;
+  CreateTemporaryTestFile(kFileSize);
+  MemoryMappedFile map;
+
+  File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
+  map.Initialize(file.Pass(), MemoryMappedFile::Region(0, kPartialSize));
+  ASSERT_EQ(kPartialSize, map.length());
+  ASSERT_TRUE(map.data() != NULL);
+  EXPECT_TRUE(map.IsValid());
+  ASSERT_TRUE(CheckBufferContents(map.data(), kPartialSize, 0));
+}
+
+TEST_F(MemoryMappedFileTest, MapPartialRegionAtEnd) {
+  const size_t kFileSize = 157 * 1024;
+  const size_t kPartialSize = 5 * 1024 - 32;
+  const size_t kOffset = kFileSize - kPartialSize;
+  CreateTemporaryTestFile(kFileSize);
+  MemoryMappedFile map;
+
+  File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
+  map.Initialize(file.Pass(), MemoryMappedFile::Region(kOffset, kPartialSize));
+  ASSERT_EQ(kPartialSize, map.length());
+  ASSERT_TRUE(map.data() != NULL);
+  EXPECT_TRUE(map.IsValid());
+  ASSERT_TRUE(CheckBufferContents(map.data(), kPartialSize, kOffset));
+}
+
+TEST_F(MemoryMappedFileTest, MapSmallPartialRegionInTheMiddle) {
+  const size_t kFileSize = 157 * 1024;
+  const size_t kOffset = 1024 * 5 + 32;
+  const size_t kPartialSize = 8;
+
+  CreateTemporaryTestFile(kFileSize);
+  MemoryMappedFile map;
+
+  File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
+  map.Initialize(file.Pass(), MemoryMappedFile::Region(kOffset, kPartialSize));
+  ASSERT_EQ(kPartialSize, map.length());
+  ASSERT_TRUE(map.data() != NULL);
+  EXPECT_TRUE(map.IsValid());
+  ASSERT_TRUE(CheckBufferContents(map.data(), kPartialSize, kOffset));
+}
+
+TEST_F(MemoryMappedFileTest, MapLargePartialRegionInTheMiddle) {
+  const size_t kFileSize = 157 * 1024;
+  const size_t kOffset = 1024 * 5 + 32;
+  const size_t kPartialSize = 16 * 1024 - 32;
+
+  CreateTemporaryTestFile(kFileSize);
+  MemoryMappedFile map;
+
+  File file(temp_file_path(), File::FLAG_OPEN | File::FLAG_READ);
+  map.Initialize(file.Pass(), MemoryMappedFile::Region(kOffset, kPartialSize));
+  ASSERT_EQ(kPartialSize, map.length());
+  ASSERT_TRUE(map.data() != NULL);
+  EXPECT_TRUE(map.IsValid());
+  ASSERT_TRUE(CheckBufferContents(map.data(), kPartialSize, kOffset));
+}
+
+}  // namespace
+
+}  // namespace base
diff --git a/base/files/memory_mapped_file_win.cc b/base/files/memory_mapped_file_win.cc
index f382287..4e7e934 100644
--- a/base/files/memory_mapped_file_win.cc
+++ b/base/files/memory_mapped_file_win.cc
@@ -18,17 +18,13 @@
   return Initialize(file_name);
 }
 
-bool MemoryMappedFile::MapFileToMemory() {
+bool MemoryMappedFile::MapFileRegionToMemory(
+    const MemoryMappedFile::Region& region) {
   ThreadRestrictions::AssertIOAllowed();
 
   if (!file_.IsValid())
     return false;
 
-  int64 len = file_.GetLength();
-  if (len <= 0 || len > kint32max)
-    return false;
-  length_ = static_cast<size_t>(len);
-
   int flags = image_ ? SEC_IMAGE | PAGE_READONLY : PAGE_READONLY;
 
   file_mapping_.Set(::CreateFileMapping(file_.GetPlatformFile(), NULL,
@@ -36,9 +32,49 @@
   if (!file_mapping_.IsValid())
     return false;
 
+  LARGE_INTEGER map_start = {0};
+  SIZE_T map_size = 0;
+  int32 data_offset = 0;
+
+  if (region == MemoryMappedFile::Region::kWholeFile) {
+    int64 file_len = file_.GetLength();
+    if (file_len <= 0 || file_len > kint32max)
+      return false;
+    length_ = static_cast<size_t>(file_len);
+  } else {
+    // The region can be arbitrarily aligned. MapViewOfFile, instead, requires
+    // that the start address is aligned to the VM granularity (which is
+    // typically larger than a page size, for instance 32k).
+    // Also, conversely to POSIX's mmap, the |map_size| doesn't have to be
+    // aligned and must be less than or equal the mapped file size.
+    // We map here the outer region [|aligned_start|, |aligned_start+size|]
+    // which contains |region| and then add up the |data_offset| displacement.
+    int64 aligned_start = 0;
+    int64 ignored = 0;
+    CalculateVMAlignedBoundaries(
+        region.offset, region.size, &aligned_start, &ignored, &data_offset);
+    int64 size = region.size + data_offset;
+
+    // Ensure that the casts below in the MapViewOfFile call are sane.
+    if (aligned_start < 0 || size < 0 ||
+        static_cast<uint64>(size) > std::numeric_limits<SIZE_T>::max()) {
+      DLOG(ERROR) << "Region bounds are not valid for MapViewOfFile";
+      return false;
+    }
+    map_start.QuadPart = aligned_start;
+    map_size = static_cast<SIZE_T>(size);
+    length_ = static_cast<size_t>(region.size);
+  }
+
   data_ = static_cast<uint8*>(::MapViewOfFile(file_mapping_.Get(),
-                                              FILE_MAP_READ, 0, 0, 0));
-  return data_ != NULL;
+                                              FILE_MAP_READ,
+                                              map_start.HighPart,
+                                              map_start.LowPart,
+                                              map_size));
+  if (data_ == NULL)
+    return false;
+  data_ += data_offset;
+  return true;
 }
 
 void MemoryMappedFile::CloseHandles() {
diff --git a/ui/base/resource/data_pack.cc b/ui/base/resource/data_pack.cc
index 8e9ee30..705c1a0 100644
--- a/ui/base/resource/data_pack.cc
+++ b/ui/base/resource/data_pack.cc
@@ -85,8 +85,15 @@
 }
 
 bool DataPack::LoadFromFile(base::File file) {
+  return LoadFromFileRegion(file.Pass(),
+                            base::MemoryMappedFile::Region::kWholeFile);
+}
+
+bool DataPack::LoadFromFileRegion(
+    base::File file,
+    const base::MemoryMappedFile::Region& region) {
   mmap_.reset(new base::MemoryMappedFile);
-  if (!mmap_->Initialize(file.Pass())) {
+  if (!mmap_->Initialize(file.Pass(), region)) {
     DLOG(ERROR) << "Failed to mmap datapack";
     UMA_HISTOGRAM_ENUMERATION("DataPack.Load", INIT_FAILED_FROM_FILE,
                               LOAD_ERRORS_COUNT);
diff --git a/ui/base/resource/data_pack.h b/ui/base/resource/data_pack.h
index 4d9d4bb..2eeb1b2 100644
--- a/ui/base/resource/data_pack.h
+++ b/ui/base/resource/data_pack.h
@@ -13,6 +13,7 @@
 
 #include "base/basictypes.h"
 #include "base/files/file.h"
+#include "base/files/memory_mapped_file.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/strings/string_piece.h"
 #include "ui/base/layout.h"
@@ -21,7 +22,6 @@
 
 namespace base {
 class FilePath;
-class MemoryMappedFile;
 class RefCountedStaticMemory;
 }
 
@@ -38,6 +38,10 @@
   // Loads a pack file from |file|, returning false on error.
   bool LoadFromFile(base::File file);
 
+  // Loads a pack file from |region| of |file|, returning false on error.
+  bool LoadFromFileRegion(base::File file,
+                          const base::MemoryMappedFile::Region& region);
+
   // Writes a pack file containing |resources| to |path|. If there are any
   // text resources to be written, their encoding must already agree to the
   // |textEncodingType| specified. If no text resources are present, please
diff --git a/ui/base/resource/data_pack_unittest.cc b/ui/base/resource/data_pack_unittest.cc
index 9583df1..6fcaeb3 100644
--- a/ui/base/resource/data_pack_unittest.cc
+++ b/ui/base/resource/data_pack_unittest.cc
@@ -92,6 +92,46 @@
   ASSERT_FALSE(pack.GetStringPiece(140, &data));
 }
 
+TEST(DataPackTest, LoadFromFileRegion) {
+  base::ScopedTempDir dir;
+  ASSERT_TRUE(dir.CreateUniqueTempDir());
+  base::FilePath data_path = dir.path().Append(FILE_PATH_LITERAL("sample.pak"));
+
+  // Construct a file which has a non page-aligned zero-filled header followed
+  // by the actual pak file content.
+  const char kPadding[5678] = {0};
+  ASSERT_EQ(static_cast<int>(sizeof(kPadding)),
+            base::WriteFile(data_path, kPadding, sizeof(kPadding)));
+  ASSERT_EQ(static_cast<int>(kSamplePakSize),
+            base::AppendToFile(data_path, kSamplePakContents, kSamplePakSize));
+
+  base::File file(data_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
+  ASSERT_TRUE(file.IsValid());
+
+  // Load the file through the data pack API.
+  DataPack pack(SCALE_FACTOR_100P);
+  base::MemoryMappedFile::Region region(sizeof(kPadding), kSamplePakSize);
+  ASSERT_TRUE(pack.LoadFromFileRegion(file.Pass(), region));
+
+  base::StringPiece data;
+  ASSERT_TRUE(pack.HasResource(4));
+  ASSERT_TRUE(pack.GetStringPiece(4, &data));
+  EXPECT_EQ("this is id 4", data);
+  ASSERT_TRUE(pack.HasResource(6));
+  ASSERT_TRUE(pack.GetStringPiece(6, &data));
+  EXPECT_EQ("this is id 6", data);
+
+  // Try reading zero-length data blobs, just in case.
+  ASSERT_TRUE(pack.GetStringPiece(1, &data));
+  EXPECT_EQ(0U, data.length());
+  ASSERT_TRUE(pack.GetStringPiece(10, &data));
+  EXPECT_EQ(0U, data.length());
+
+  // Try looking up an invalid key.
+  ASSERT_FALSE(pack.HasResource(140));
+  ASSERT_FALSE(pack.GetStringPiece(140, &data));
+}
+
 INSTANTIATE_TEST_CASE_P(WriteBINARY, DataPackTest, ::testing::Values(
     DataPack::BINARY));
 INSTANTIATE_TEST_CASE_P(WriteUTF8, DataPackTest, ::testing::Values(
diff --git a/ui/base/resource/resource_bundle.cc b/ui/base/resource/resource_bundle.cc
index f5a592d..c070585 100644
--- a/ui/base/resource/resource_bundle.cc
+++ b/ui/base/resource/resource_bundle.cc
@@ -234,9 +234,17 @@
 
 void ResourceBundle::AddDataPackFromFile(base::File file,
                                          ScaleFactor scale_factor) {
+  AddDataPackFromFileRegion(
+      file.Pass(), base::MemoryMappedFile::Region::kWholeFile, scale_factor);
+}
+
+void ResourceBundle::AddDataPackFromFileRegion(
+    base::File file,
+    const base::MemoryMappedFile::Region& region,
+    ScaleFactor scale_factor) {
   scoped_ptr<DataPack> data_pack(
       new DataPack(scale_factor));
-  if (data_pack->LoadFromFile(file.Pass())) {
+  if (data_pack->LoadFromFileRegion(file.Pass(), region)) {
     AddDataPack(data_pack.release());
   } else {
     LOG(ERROR) << "Failed to load data pack from file."
diff --git a/ui/base/resource/resource_bundle.h b/ui/base/resource/resource_bundle.h
index 060ea6c..c17ff30 100644
--- a/ui/base/resource/resource_bundle.h
+++ b/ui/base/resource/resource_bundle.h
@@ -10,6 +10,7 @@
 
 #include "base/basictypes.h"
 #include "base/files/file_path.h"
+#include "base/files/memory_mapped_file.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/scoped_vector.h"
@@ -165,6 +166,11 @@
   // Same as above but using an already open file.
   void AddDataPackFromFile(base::File file, ScaleFactor scale_factor);
 
+  // Same as above but using only a region (offset + size) of the file.
+  void AddDataPackFromFileRegion(base::File file,
+                                 const base::MemoryMappedFile::Region& region,
+                                 ScaleFactor scale_factor);
+
   // Same as AddDataPackFromPath but does not log an error if the pack fails to
   // load.
   void AddOptionalDataPackFromPath(const base::FilePath& path,