diff --git a/packages/url_launcher/url_launcher_windows/CHANGELOG.md b/packages/url_launcher/url_launcher_windows/CHANGELOG.md index 13405529be9..668ee6eff6a 100644 --- a/packages/url_launcher/url_launcher_windows/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_windows/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 3.1.3 * Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. +* Fixes handling of `file:` URLs that contain UTF-8 encoded paths. ## 3.1.2 diff --git a/packages/url_launcher/url_launcher_windows/pubspec.yaml b/packages/url_launcher/url_launcher_windows/pubspec.yaml index addd5363905..629e9dc7196 100644 --- a/packages/url_launcher/url_launcher_windows/pubspec.yaml +++ b/packages/url_launcher/url_launcher_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_windows description: Windows implementation of the url_launcher plugin. repository: https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.1.2 +version: 3.1.3 environment: sdk: ^3.3.0 diff --git a/packages/url_launcher/url_launcher_windows/windows/CMakeLists.txt b/packages/url_launcher/url_launcher_windows/windows/CMakeLists.txt index da39522c562..2b07f3a75a0 100644 --- a/packages/url_launcher/url_launcher_windows/windows/CMakeLists.txt +++ b/packages/url_launcher/url_launcher_windows/windows/CMakeLists.txt @@ -25,7 +25,7 @@ set_target_properties(${PLUGIN_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden) target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) target_include_directories(${PLUGIN_NAME} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") -target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin) +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin shlwapi.lib) # List of absolute paths to libraries that should be bundled with the plugin set(file_chooser_bundled_libraries @@ -62,7 +62,7 @@ add_executable(${TEST_RUNNER} ) apply_standard_settings(${TEST_RUNNER}) target_include_directories(${TEST_RUNNER} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") -target_link_libraries(${TEST_RUNNER} PRIVATE flutter_wrapper_plugin) +target_link_libraries(${TEST_RUNNER} PRIVATE flutter_wrapper_plugin shlwapi.lib) target_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock) # flutter_wrapper_plugin has link dependencies on the Flutter DLL. add_custom_command(TARGET ${TEST_RUNNER} POST_BUILD diff --git a/packages/url_launcher/url_launcher_windows/windows/test/url_launcher_windows_test.cpp b/packages/url_launcher/url_launcher_windows/windows/test/url_launcher_windows_test.cpp index 26729574000..db46e1a4a98 100644 --- a/packages/url_launcher/url_launcher_windows/windows/test/url_launcher_windows_test.cpp +++ b/packages/url_launcher/url_launcher_windows/windows/test/url_launcher_windows_test.cpp @@ -22,10 +22,12 @@ namespace { using flutter::EncodableMap; using flutter::EncodableValue; +using ::testing::_; using ::testing::DoAll; using ::testing::Pointee; using ::testing::Return; using ::testing::SetArgPointee; +using ::testing::StrEq; class MockSystemApis : public SystemApis { public: @@ -135,5 +137,28 @@ TEST(UrlLauncherPlugin, LaunchReportsError) { EXPECT_TRUE(result.has_error()); } +TEST(UrlLauncherPlugin, LaunchUTF8EncodedFileURLSuccess) { + std::unique_ptr system = std::make_unique(); + + // Return a success value (>32) from launching. + EXPECT_CALL( + *system, + ShellExecuteW( + _, StrEq(L"open"), + // 家の管理/スキャナ"), + StrEq( + L"file:///G:/\x5bb6\x306e\x7ba1\x7406/\x30b9\x30ad\x30e3\x30ca"), + _, _, _)) + .WillOnce(Return(reinterpret_cast(33))); + + UrlLauncherPlugin plugin(std::move(system)); + ErrorOr result = plugin.LaunchUrl( + "file:///G:/%E5%AE%B6%E3%81%AE%E7%AE%A1%E7%90%86/" + "%E3%82%B9%E3%82%AD%E3%83%A3%E3%83%8A"); + + ASSERT_FALSE(result.has_error()); + EXPECT_TRUE(result.value()); +} + } // namespace test } // namespace url_launcher_windows diff --git a/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp b/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp index c8e7b2f516c..b737b4579b8 100644 --- a/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp +++ b/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -98,7 +99,19 @@ ErrorOr UrlLauncherPlugin::CanLaunchUrl(const std::string& url) { } ErrorOr UrlLauncherPlugin::LaunchUrl(const std::string& url) { - std::wstring url_wide = Utf16FromUtf8(url); + std::wstring url_wide; + if (url.find("file:") == 0) { + // ShellExecuteW does not process %-encoded UTF8 strings in file URLs. + DWORD unescaped_len = 0; + std::string unescaped_url = url; + if (FAILED(::UrlUnescapeA(unescaped_url.data(), /*pszUnescaped=*/nullptr, + &unescaped_len, URL_UNESCAPE_INPLACE))) { + return FlutterError("open_error", "Failed to unescape file URL"); + } + url_wide = Utf16FromUtf8(unescaped_url); + } else { + url_wide = Utf16FromUtf8(url); + } int status = static_cast(reinterpret_cast( system_apis_->ShellExecuteW(nullptr, TEXT("open"), url_wide.c_str(),