summaryrefslogtreecommitdiff
path: root/chromium/ui/base/resource/resource_bundle_android.cc
blob: a91fdabe9644c0ff93e9d1ceaf8236f41a80a662 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
// 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 "ui/base/resource/resource_bundle_android.h"

#include "base/android/apk_assets.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/path_service.h"
#include "jni/ResourceBundle_jni.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/data_pack.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/ui_base_paths.h"

namespace ui {

namespace {

bool g_locale_paks_in_apk = false;
bool g_load_secondary_locale_paks = false;
// It is okay to cache and share these file descriptors since the
// ResourceBundle singleton never closes the handles.
int g_chrome_100_percent_fd = -1;
int g_resources_pack_fd = -1;
int g_locale_pack_fd = -1;
int g_secondary_locale_pack_fd = -1;
base::MemoryMappedFile::Region g_chrome_100_percent_region;
base::MemoryMappedFile::Region g_resources_pack_region;
base::MemoryMappedFile::Region g_locale_pack_region;
base::MemoryMappedFile::Region g_secondary_locale_pack_region;

bool LoadFromApkOrFile(const char* apk_path,
                       const base::FilePath* disk_path,
                       int* out_fd,
                       base::MemoryMappedFile::Region* out_region) {
  DCHECK_EQ(*out_fd, -1) << "Attempt to load " << apk_path << " twice.";
  if (apk_path != nullptr) {
    *out_fd = base::android::OpenApkAsset(apk_path, out_region);
  }
  // For unit tests, the file exists on disk.
  if (*out_fd < 0 && disk_path != nullptr) {
    int flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
    *out_fd = base::File(*disk_path, flags).TakePlatformFile();
    *out_region = base::MemoryMappedFile::Region::kWholeFile;
  }
  bool success = *out_fd >= 0;
  if (!success) {
    LOG(ERROR) << "Failed to open pak file: " << apk_path;
  }
  return success;
}

int LoadLocalePakFromApk(const std::string& app_locale,
                         base::MemoryMappedFile::Region* out_region) {
  std::string locale_path_within_apk =
      GetPathForAndroidLocalePakWithinApk(app_locale);
  if (locale_path_within_apk.empty()) {
    LOG(WARNING) << "locale_path_within_apk.empty() for locale "
                 << app_locale;
    return -1;
  }
  return base::android::OpenApkAsset(locale_path_within_apk, out_region);
}

std::unique_ptr<DataPack> LoadDataPackFromLocalePak(
    int locale_pack_fd,
    const base::MemoryMappedFile::Region& region) {
  auto data_pack = std::make_unique<DataPack>(SCALE_FACTOR_100P);
  if (!data_pack->LoadFromFileRegion(base::File(locale_pack_fd), region)) {
    LOG(WARNING) << "failed to load locale.pak";
    NOTREACHED();
    return nullptr;
  }
  return data_pack;
}

}  // namespace

void ResourceBundle::LoadCommonResources() {
  base::FilePath disk_path;
  PathService::Get(ui::DIR_RESOURCE_PAKS_ANDROID, &disk_path);
  disk_path = disk_path.AppendASCII("chrome_100_percent.pak");
  if (LoadFromApkOrFile("assets/chrome_100_percent.pak",
                        &disk_path,
                        &g_chrome_100_percent_fd,
                        &g_chrome_100_percent_region)) {
    AddDataPackFromFileRegion(base::File(g_chrome_100_percent_fd),
                              g_chrome_100_percent_region, SCALE_FACTOR_100P);
  }
}

bool ResourceBundle::LocaleDataPakExists(const std::string& locale) {
  if (g_locale_paks_in_apk) {
    return !GetPathForAndroidLocalePakWithinApk(locale).empty();
  }
  return !GetLocaleFilePath(locale, true).empty();
}

std::string ResourceBundle::LoadLocaleResources(
    const std::string& pref_locale) {
  DCHECK(!locale_resources_data_.get() &&
         !secondary_locale_resources_data_.get())
             << "locale.pak already loaded";
  if (g_locale_pack_fd != -1) {
    LOG(WARNING)
        << "Unexpected (outside of tests): Loading a second locale pak file.";
  }
  std::string app_locale = l10n_util::GetApplicationLocale(pref_locale);

  // Load primary locale .pak file.
  if (g_locale_paks_in_apk) {
    g_locale_pack_fd = LoadLocalePakFromApk(app_locale, &g_locale_pack_region);
  } else {
    base::FilePath locale_file_path = GetOverriddenPakPath();
    if (locale_file_path.empty())
      locale_file_path = GetLocaleFilePath(app_locale, true);

    if (locale_file_path.empty()) {
      // It's possible that there is no locale.pak.
      LOG(WARNING) << "locale_file_path.empty() for locale " << app_locale;
      return std::string();
    }
    int flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
    g_locale_pack_fd = base::File(locale_file_path, flags).TakePlatformFile();
    g_locale_pack_region = base::MemoryMappedFile::Region::kWholeFile;
  }

  locale_resources_data_ = LoadDataPackFromLocalePak(
      g_locale_pack_fd, g_locale_pack_region);

  if (!locale_resources_data_.get())
    return std::string();

  // Load secondary locale .pak file if it exists. For debug build monochrome,
  // a secondary locale pak will always be loaded; however, it should be
  // unnecessary for loading locale resources because the primary locale pak
  // would have a copy of all the resources in the secondary locale pak.
  if (g_load_secondary_locale_paks) {
    g_secondary_locale_pack_fd = LoadLocalePakFromApk(
        app_locale, &g_secondary_locale_pack_region);

    secondary_locale_resources_data_ = LoadDataPackFromLocalePak(
        g_secondary_locale_pack_fd, g_secondary_locale_pack_region);

    if (!secondary_locale_resources_data_.get())
      return std::string();
  }

  return app_locale;
}

gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) {
  return GetImageNamed(resource_id);
}

void SetLocalePaksStoredInApk(bool value) {
  g_locale_paks_in_apk = value;
}

void SetLoadSecondaryLocalePaks(bool value) {
  g_load_secondary_locale_paks = value;
}

void LoadMainAndroidPackFile(const char* path_within_apk,
                             const base::FilePath& disk_file_path) {
  if (LoadFromApkOrFile(path_within_apk,
                        &disk_file_path,
                        &g_resources_pack_fd,
                        &g_resources_pack_region)) {
    ResourceBundle::GetSharedInstance().AddDataPackFromFileRegion(
        base::File(g_resources_pack_fd), g_resources_pack_region,
        SCALE_FACTOR_NONE);
  }
}

int GetMainAndroidPackFd(base::MemoryMappedFile::Region* out_region) {
  DCHECK_GE(g_resources_pack_fd, 0);
  *out_region = g_resources_pack_region;
  return g_resources_pack_fd;
}

int GetCommonResourcesPackFd(base::MemoryMappedFile::Region* out_region) {
  DCHECK_GE(g_chrome_100_percent_fd, 0);
  *out_region = g_chrome_100_percent_region;
  return g_chrome_100_percent_fd;
}

int GetLocalePackFd(base::MemoryMappedFile::Region* out_region) {
  DCHECK_GE(g_locale_pack_fd, 0);
  *out_region = g_locale_pack_region;
  return g_locale_pack_fd;
}

int GetSecondaryLocalePackFd(base::MemoryMappedFile::Region* out_region) {
  *out_region = g_secondary_locale_pack_region;
  return g_secondary_locale_pack_fd;
}

std::string GetPathForAndroidLocalePakWithinApk(const std::string& locale) {
  JNIEnv* env = base::android::AttachCurrentThread();
  base::android::ScopedJavaLocalRef<jstring> ret =
      Java_ResourceBundle_getLocalePakResourcePath(
          env, base::android::ConvertUTF8ToJavaString(env, locale));
  if (ret.obj() == nullptr) {
    return std::string();
  }
  return base::android::ConvertJavaStringToUTF8(env, ret.obj());
}

}  // namespace ui