Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit e885e16

Browse files
committed
Wires the locale provided by Fuchsia.
The FIDL service `fuchsia.intl.PropertyProvider` is a service that the flutter runner can use to obtain information on system preferred locales. This change sends a platform message "setLocale" on the channel "flutter/localization", based on the values provided by the above mentioned FIDL service. Credit: most of this was initially written by @kpozin; I ported it to out-of-tree flutter engine. Tested: 1. Compile and publish the unit tests package as shown in the script below. 2. In a Fuchsia repository (pointed to by `$FUCHSIA_DIR`), run `fx serve` 3. `fx shell run fuchsia-pkg://fuchsia.com/flutter_runner_tests#meta/flutter_runner_tests.cmx` The script used to update the unit tests. ```bash #!/bin/bash set -x # You will need to ensure that $FLUTTER_ENGINE_DIR and $FUCHSIA_DIR # are set to appropriate values for your machine. FLUTTER_ENGINE_DIR="${FLUTTER_ENGINE_DIR:-$HOME/fx/flutter/engine/src}" readonly OUT_DIR="${FLUTTER_ENGINE_DIR}/out" ( cd ${FLUTTER_ENGINE_DIR} ./flutter/tools/gn --fuchsia --fuchsia-cpu x64 --unoptimized ninja -j 100 -C "${OUT_DIR}/fuchsia_debug_unopt_x64" cp "${OUT_DIR}/compile_commands.json" "${FLUTTER_ENGINE_DIR}" echo "Publishing the tests package" "${FLUTTER_ENGINE_DIR}/fuchsia/sdk/linux/tools/pm" publish \ -a -r $FUCHSIA_DIR/out/release/amber-files \ -f "${FLUTTER_ENGINE_DIR}/out/fuchsia_debug_unopt_x64/flutter_runner_tests-0.far" ) ```
1 parent e3b5d8e commit e885e16

File tree

10 files changed

+328
-5
lines changed

10 files changed

+328
-5
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1420,4 +1420,37 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14201420
See the License for the specific language governing permissions and
14211421
limitations under the License.
14221422
====================================================================================================
1423-
Total license count: 2
1423+
LIBRARY: engine
1424+
ORIGIN: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_intl.cc + ../../../flutter/LICENSE
1425+
TYPE: LicenseType.bsd
1426+
FILE: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_intl.cc
1427+
FILE: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_intl.h
1428+
FILE: ../../../flutter/shell/platform/fuchsia/flutter/fuchsia_intl_unittest.cc
1429+
----------------------------------------------------------------------------------------------------
1430+
Copyright 2019 The Flutter Authors. All rights reserved.
1431+
1432+
Redistribution and use in source and binary forms, with or without modification,
1433+
are permitted provided that the following conditions are met:
1434+
1435+
* Redistributions of source code must retain the above copyright
1436+
notice, this list of conditions and the following disclaimer.
1437+
* Redistributions in binary form must reproduce the above
1438+
copyright notice, this list of conditions and the following
1439+
disclaimer in the documentation and/or other materials provided
1440+
with the distribution.
1441+
* Neither the name of Google Inc. nor the names of its
1442+
contributors may be used to endorse or promote products derived
1443+
from this software without specific prior written permission.
1444+
1445+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
1446+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1447+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1448+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
1449+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1450+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
1451+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
1452+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1453+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
1454+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1455+
====================================================================================================
1456+
Total license count: 3

shell/platform/fuchsia/flutter/BUILD.gn

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,9 @@ executable("flutter_runner_unittests") {
256256
"accessibility_bridge.h",
257257
"accessibility_bridge_unittest.cc",
258258
"flutter_runner_fakes.h",
259+
"fuchsia_intl.cc",
260+
"fuchsia_intl.h",
261+
"fuchsia_intl_unittest.cc",
259262
"logging.h",
260263
"platform_view.cc",
261264
"platform_view.h",
@@ -298,6 +301,13 @@ fuchsia_archive("flutter_runner_tests") {
298301

299302
binary = "$target_name"
300303

304+
resources = [
305+
{
306+
path = rebase_path("//third_party/icu/common/icudtl.dat")
307+
dest = "icudtl.dat"
308+
},
309+
]
310+
301311
meta_dir = "$flutter_root/shell/platform/fuchsia/flutter/meta"
302312

303313
libraries = common_libs

shell/platform/fuchsia/flutter/engine.cc

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "engine.h"
66

77
#include <lib/async/cpp/task.h>
8+
#include <zircon/status.h>
89
#include <sstream>
910

1011
#include "flutter/common/task_runners.h"
@@ -14,6 +15,7 @@
1415
#include "flutter/runtime/dart_vm_lifecycle.h"
1516
#include "flutter/shell/common/rasterizer.h"
1617
#include "flutter/shell/common/run_configuration.h"
18+
#include "fuchsia_intl.h"
1719
#include "platform_view.h"
1820
#include "runtime/dart/utils/files.h"
1921
#include "task_runner_adapter.h"
@@ -39,6 +41,13 @@ static void UpdateNativeThreadLabelNames(const std::string& label,
3941
set_thread_name(runners.GetIOTaskRunner(), label, ".io");
4042
}
4143

44+
static fml::RefPtr<flutter::PlatformMessage> MakeLocalizationPlatformMessage(
45+
const fuchsia::intl::Profile& intl_profile) {
46+
return fml::MakeRefCounted<flutter::PlatformMessage>(
47+
"flutter/localization", MakeLocalizationPlatformMessageData(intl_profile),
48+
nullptr);
49+
}
50+
4251
Engine::Engine(Delegate& delegate,
4352
std::string thread_label,
4453
std::shared_ptr<sys::ServiceDirectory> svc,
@@ -110,8 +119,7 @@ Engine::Engine(Delegate& delegate,
110119
on_create_platform_view = fml::MakeCopyable(
111120
[debug_label = thread_label_,
112121
view_ref_control = std::move(view_ref_control),
113-
view_ref = std::move(view_ref),
114-
runner_services = std::move(runner_services),
122+
view_ref = std::move(view_ref), runner_services,
115123
parent_environment_service_provider =
116124
std::move(parent_environment_service_provider),
117125
session_listener_request = std::move(session_listener_request),
@@ -256,6 +264,49 @@ Engine::Engine(Delegate& delegate,
256264
// notification. Fire one eagerly.
257265
shell_->GetPlatformView()->NotifyCreated();
258266

267+
// Connect to the intl property provider.
268+
{
269+
intl_property_provider_.set_error_handler([](zx_status_t status) {
270+
FML_LOG(ERROR) << "Failed to connect to "
271+
<< fuchsia::intl::PropertyProvider::Name_ << ": "
272+
<< zx_status_get_string(status);
273+
});
274+
275+
// Note that we're using the runner's services, not the component's.
276+
// Flutter locales should be updated regardless of whether the component has
277+
// direct access to the fuchsia.intl.PropertyProvider service.
278+
ZX_ASSERT(runner_services->Connect(intl_property_provider_.NewRequest()) ==
279+
ZX_OK);
280+
281+
auto get_profile_callback = [flutter_runner_engine =
282+
weak_factory_.GetWeakPtr()](
283+
const fuchsia::intl::Profile& profile) {
284+
if (!flutter_runner_engine) {
285+
return;
286+
}
287+
if (!profile.has_locales()) {
288+
FML_LOG(WARNING) << "Got intl Profile without locales";
289+
}
290+
auto message = MakeLocalizationPlatformMessage(profile);
291+
FML_VLOG(-1) << "Sending LocalizationPlatformMessage";
292+
flutter_runner_engine->shell_->GetPlatformView()->DispatchPlatformMessage(
293+
message);
294+
};
295+
296+
FML_VLOG(-1) << "Requesting intl Profile";
297+
298+
// Make the initial request
299+
intl_property_provider_->GetProfile(get_profile_callback);
300+
301+
// And register for changes
302+
intl_property_provider_.events().OnChange = [this, runner_services,
303+
get_profile_callback]() {
304+
FML_VLOG(-1) << fuchsia::intl::PropertyProvider::Name_ << ": OnChange";
305+
runner_services->Connect(intl_property_provider_.NewRequest());
306+
intl_property_provider_->GetProfile(get_profile_callback);
307+
};
308+
}
309+
259310
// Launch the engine in the appropriate configuration.
260311
auto run_configuration = flutter::RunConfiguration::InferFromSettings(
261312
settings_, task_runners.GetIOTaskRunner());

shell/platform/fuchsia/flutter/engine.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#ifndef FLUTTER_SHELL_PLATFORM_FUCHSIA_ENGINE_H_
66
#define FLUTTER_SHELL_PLATFORM_FUCHSIA_ENGINE_H_
77

8+
#include <fuchsia/intl/cpp/fidl.h>
89
#include <fuchsia/io/cpp/fidl.h>
910
#include <fuchsia/ui/gfx/cpp/fidl.h>
1011
#include <fuchsia/ui/views/cpp/fidl.h>
@@ -58,6 +59,8 @@ class Engine final {
5859
std::unique_ptr<flutter::Shell> shell_;
5960
zx::event vsync_event_;
6061
fml::WeakPtrFactory<Engine> weak_factory_;
62+
// A stub for the FIDL protocol fuchsia.intl.PropertyProvider.
63+
fuchsia::intl::PropertyProviderPtr intl_property_provider_;
6164

6265
void OnMainIsolateStart();
6366

shell/platform/fuchsia/flutter/engine_flutter_runner.gni

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ template("flutter_runner") {
5050
"compositor_context.h",
5151
"engine.cc",
5252
"engine.h",
53+
"fuchsia_intl.cc",
54+
"fuchsia_intl.h",
5355
"isolate_configurator.cc",
5456
"isolate_configurator.h",
5557
"logging.h",
@@ -111,6 +113,7 @@ template("flutter_runner") {
111113
"$fuchsia_sdk_root/fidl:fuchsia.accessibility.semantics",
112114
"$fuchsia_sdk_root/fidl:fuchsia.fonts",
113115
"$fuchsia_sdk_root/fidl:fuchsia.images",
116+
"$fuchsia_sdk_root/fidl:fuchsia.intl",
114117
"$fuchsia_sdk_root/fidl:fuchsia.io",
115118
"$fuchsia_sdk_root/fidl:fuchsia.modular",
116119
"$fuchsia_sdk_root/fidl:fuchsia.sys",
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// Copyright 2019 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "fuchsia_intl.h"
6+
7+
#include <sstream>
8+
#include <string>
9+
#include <vector>
10+
11+
#include "loop.h"
12+
#include "rapidjson/document.h"
13+
#include "rapidjson/stringbuffer.h"
14+
#include "rapidjson/writer.h"
15+
#include "runner.h"
16+
#include "runtime/dart/utils/tempfs.h"
17+
#include "third_party/icu/source/common/unicode/bytestream.h"
18+
#include "third_party/icu/source/common/unicode/errorcode.h"
19+
#include "third_party/icu/source/common/unicode/locid.h"
20+
#include "third_party/icu/source/common/unicode/strenum.h"
21+
#include "third_party/icu/source/common/unicode/stringpiece.h"
22+
#include "third_party/icu/source/common/unicode/uloc.h"
23+
24+
namespace {
25+
26+
using icu::Locale;
27+
28+
// This backfills for the static method icu::Locale::forLanguageTag which is
29+
// missing in ICU versions prior to 63.
30+
//
31+
// The implementation of the ICU version is here:
32+
// https://sourcegraph.com/github.com/unicode-org/icu@64b58ccda315902b7b88e32a12ffca3b7b99cdf0/-/blob/icu4c/source/common/unicode/locid.h#L408:29
33+
//
34+
// This implementation differs in that it must use the C API to obtain the
35+
// locale, and that it initializes the default locale in the process. The C++
36+
// version does not need to do either, but it achieves this by accessing the
37+
// private API for icu::Locale, which we can not duplicate. We are also using
38+
// the properties of the fuchsia.intl.ProfileProvider not to have to handle
39+
// certain edge cases.
40+
static icu::Locale forLanguageTag(const std::string& tag, UErrorCode& status) {
41+
// The ICU library has a special "bogus" locale, which we do not have.
42+
icu::Locale bogus;
43+
if (U_FAILURE(status)) {
44+
return bogus;
45+
}
46+
// This is for the "preflight" mode, in which we first guess at the buffer
47+
// size and parse; and retry the parse in case the buffer overflowed on the
48+
// first go, based on the size information returned from the preflight.
49+
const int32_t initial_size = 100;
50+
std::vector<char> buffer(initial_size);
51+
int32_t parsed_size = 0;
52+
uloc_forLanguageTag(tag.c_str(), buffer.data(), buffer.size(), &parsed_size,
53+
&status);
54+
if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) {
55+
// This is not recoverable, exit.
56+
return bogus;
57+
}
58+
buffer.resize(parsed_size);
59+
if (status == U_BUFFER_OVERFLOW_ERROR) {
60+
// If the locale was too long to fit into the buffer, since now we know
61+
// the parsed size and have resized the buffer, we can parse again.
62+
status = U_ZERO_ERROR;
63+
uloc_forLanguageTag(tag.c_str(), buffer.data(), buffer.size(), &parsed_size,
64+
&status);
65+
}
66+
if (U_FAILURE(status)) {
67+
// This time we can not recover, so exit.
68+
return bogus;
69+
}
70+
std::string locale_string(begin(buffer), end(buffer));
71+
// Strip the POSIX @-suffix that we may get after resolving BCP-47 locale
72+
// IDs like "en-US-u-ca-gregory-fw-sun-...".
73+
const size_t locale_end = locale_string.find("@");
74+
locale_string = locale_string.substr(0, locale_end);
75+
return icu::Locale::createCanonical(locale_string.c_str());
76+
}
77+
78+
} // namespace
79+
80+
namespace flutter_runner {
81+
82+
using fuchsia::intl::Profile;
83+
84+
std::vector<uint8_t> MakeLocalizationPlatformMessageData(
85+
const Profile& intl_profile) {
86+
rapidjson::Document document;
87+
auto& allocator = document.GetAllocator();
88+
document.SetObject();
89+
document.AddMember("method", "setLocale", allocator);
90+
rapidjson::Value args(rapidjson::kArrayType);
91+
92+
for (const auto& locale_id : intl_profile.locales()) {
93+
UErrorCode error_code = U_ZERO_ERROR;
94+
icu::Locale locale = forLanguageTag(locale_id.id, error_code);
95+
if (U_FAILURE(error_code)) {
96+
FML_LOG(ERROR) << "Error parsing locale ID \"" << locale_id.id << "\"";
97+
continue;
98+
}
99+
args.PushBack(rapidjson::Value().SetString(locale.getLanguage(), allocator),
100+
allocator);
101+
102+
auto country = locale.getCountry() != nullptr ? locale.getCountry() : "";
103+
args.PushBack(rapidjson::Value().SetString(country, allocator), allocator);
104+
105+
auto script = locale.getScript() != nullptr ? locale.getScript() : "";
106+
args.PushBack(rapidjson::Value().SetString(script, allocator), allocator);
107+
108+
std::string variant =
109+
locale.getVariant() != nullptr ? locale.getVariant() : "";
110+
// ICU4C capitalizes the variant for backward compatibility, even though
111+
// the preferred form is lowercase. So we lowercase here.
112+
std::transform(begin(variant), end(variant), begin(variant),
113+
[](unsigned char c) { return std::tolower(c); });
114+
args.PushBack(rapidjson::Value().SetString(variant, allocator), allocator);
115+
}
116+
117+
document.AddMember("args", args, allocator);
118+
119+
rapidjson::StringBuffer buffer;
120+
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
121+
document.Accept(writer);
122+
auto data = reinterpret_cast<const uint8_t*>(buffer.GetString());
123+
return std::vector<uint8_t>(data, data + buffer.GetSize());
124+
}
125+
126+
} // namespace flutter_runner
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2019 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef FLUTTER_SHELL_PLATFORM_FUCHSIA_FUCHSIA_INTL_H_
6+
#define FLUTTER_SHELL_PLATFORM_FUCHSIA_FUCHSIA_INTL_H_
7+
8+
#include <fuchsia/intl/cpp/fidl.h>
9+
10+
namespace flutter_runner {
11+
12+
// Make a byte vector containing the JSON string used for a localization
13+
// PlatformMessage, using the locale list in the given Profile.
14+
//
15+
// This method does not return a `fml::RefPtr<flutter::PlatformMessage>` for
16+
// testing convenience; that would require an unreasonably large set of
17+
// dependencies for the unit tests.
18+
std::vector<uint8_t> MakeLocalizationPlatformMessageData(
19+
const fuchsia::intl::Profile& intl_profile);
20+
21+
} // namespace flutter_runner
22+
23+
#endif // FLUTTER_SHELL_PLATFORM_FUCHSIA_FUCHSIA_INTL_H_

0 commit comments

Comments
 (0)