Commit 5a86cf44 authored by Marijn Kruisselbrink's avatar Marijn Kruisselbrink Committed by Commit Bot

[Native File System] Add dialog confirming read access to a directory.

See https://storage.cloud.google.com/chromium-translation-screenshots/a4d4621b4680a1742fd3ca8366e8e1e886dfef27
for what this dialog looks like.

Bug: 878585
Change-Id: I31c99e56ac4b69b5373f79992fb8179001b26dac
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1680305
Commit-Queue: Marijn Kruisselbrink <mek@chromium.org>
Reviewed-by: default avatarElly Fong-Jones <ellyjones@chromium.org>
Reviewed-by: default avatarOvidio de Jesús Ruiz-Henríquez <odejesush@chromium.org>
Cr-Commit-Position: refs/heads/master@{#676173}
parent 5615d8ff
...@@ -9554,6 +9554,14 @@ Please help our engineers fix this problem. Tell us what happened right before y ...@@ -9554,6 +9554,14 @@ Please help our engineers fix this problem. Tell us what happened right before y
Choose a different folder Choose a different folder
</message> </message>
<!-- Native File System directory access confirmation dialog. -->
<message name="IDS_NATIVE_FILE_SYSTEM_DIRECTORY_ACCESS_CONFIRMATION_TITLE" desc="Title of dialog asking user if they intended to share a particular directory">
Let site read this folder?
</message>
<message name="IDS_NATIVE_FILE_SYSTEM_DIRECTORY_ACCESS_CONFIRMATION_TEXT" desc="Text of dialog asking user if they intended to share a particular directory">
<ph name="ORIGIN">$1<ex>example.com</ex></ph> will be able to read all files in the following folder. This site can see any changes to the folder only while this tab is open.
</message>
<!-- Relaunch notification bubble and dialog. --> <!-- Relaunch notification bubble and dialog. -->
<if expr="not is_android"> <if expr="not is_android">
<if expr="not chromeos"> <if expr="not chromeos">
......
...@@ -2816,6 +2816,8 @@ jumbo_split_static_library("ui") { ...@@ -2816,6 +2816,8 @@ jumbo_split_static_library("ui") {
"views/media_router/web_contents_display_observer_view.h", "views/media_router/web_contents_display_observer_view.h",
"views/native_file_system/native_file_system_access_icon_view.cc", "views/native_file_system/native_file_system_access_icon_view.cc",
"views/native_file_system/native_file_system_access_icon_view.h", "views/native_file_system/native_file_system_access_icon_view.h",
"views/native_file_system/native_file_system_directory_access_confirmation_view.cc",
"views/native_file_system/native_file_system_directory_access_confirmation_view.h",
"views/native_file_system/native_file_system_permission_view.cc", "views/native_file_system/native_file_system_permission_view.cc",
"views/native_file_system/native_file_system_permission_view.h", "views/native_file_system/native_file_system_permission_view.h",
"views/native_file_system/native_file_system_restricted_directory_dialog_view.cc", "views/native_file_system/native_file_system_restricted_directory_dialog_view.cc",
......
...@@ -47,4 +47,15 @@ void ShowNativeFileSystemRestrictedDirectoryDialog( ...@@ -47,4 +47,15 @@ void ShowNativeFileSystemRestrictedDirectoryDialog(
// if the dialog was instantly dismissed. // if the dialog was instantly dismissed.
std::move(callback).Run(); std::move(callback).Run();
} }
void ShowNativeFileSystemDirectoryAccessConfirmationDialog(
const url::Origin& origin,
const base::FilePath& path,
base::OnceCallback<void(PermissionAction result)> callback,
content::WebContents* web_contents) {
// There's no dialog version of this available outside views, run callback as
// if the dialog was instantly dismissed.
std::move(callback).Run(PermissionAction::DISMISSED);
}
#endif // !defined(TOOLKIT_VIEWS) #endif // !defined(TOOLKIT_VIEWS)
...@@ -322,4 +322,13 @@ void ShowNativeFileSystemRestrictedDirectoryDialog( ...@@ -322,4 +322,13 @@ void ShowNativeFileSystemRestrictedDirectoryDialog(
base::OnceClosure callback, base::OnceClosure callback,
content::WebContents* web_contents); content::WebContents* web_contents);
// Displays a dialog to confirm that the user intended to give read access to a
// specific directory. Similar to ShowFolderUploadConfirmationDialog above,
// except for use by the Native File System API.
void ShowNativeFileSystemDirectoryAccessConfirmationDialog(
const url::Origin& origin,
const base::FilePath& path,
base::OnceCallback<void(PermissionAction result)> callback,
content::WebContents* web_contents);
#endif // CHROME_BROWSER_UI_BROWSER_DIALOGS_H_ #endif // CHROME_BROWSER_UI_BROWSER_DIALOGS_H_
// Copyright 2019 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 "chrome/browser/ui/views/native_file_system/native_file_system_directory_access_confirmation_view.h"
#include "base/files/file_path.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/chrome_typography.h"
#include "chrome/grit/generated_resources.h"
#include "components/constrained_window/constrained_window_views.h"
#include "components/url_formatter/elide_url.h"
#include "components/vector_icons/vector_icons.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/styled_label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/window/dialog_client_view.h"
NativeFileSystemDirectoryAccessConfirmationView::
~NativeFileSystemDirectoryAccessConfirmationView() {
// Make sure the dialog ends up calling the callback no matter what.
if (!callback_.is_null())
Close();
}
// static
views::Widget* NativeFileSystemDirectoryAccessConfirmationView::ShowDialog(
const url::Origin& origin,
const base::FilePath& path,
base::OnceCallback<void(PermissionAction result)> callback,
content::WebContents* web_contents) {
auto delegate =
base::WrapUnique(new NativeFileSystemDirectoryAccessConfirmationView(
origin, path, std::move(callback)));
return constrained_window::ShowWebModalDialogViews(delegate.release(),
web_contents);
}
base::string16 NativeFileSystemDirectoryAccessConfirmationView::GetWindowTitle()
const {
return l10n_util::GetStringUTF16(
IDS_NATIVE_FILE_SYSTEM_DIRECTORY_ACCESS_CONFIRMATION_TITLE);
}
int NativeFileSystemDirectoryAccessConfirmationView::GetDefaultDialogButton()
const {
return ui::DIALOG_BUTTON_NONE;
}
base::string16
NativeFileSystemDirectoryAccessConfirmationView::GetDialogButtonLabel(
ui::DialogButton button) const {
if (button == ui::DIALOG_BUTTON_OK)
return l10n_util::GetStringUTF16(IDS_PERMISSION_ALLOW);
return l10n_util::GetStringUTF16(IDS_APP_CANCEL);
}
bool NativeFileSystemDirectoryAccessConfirmationView::ShouldShowCloseButton()
const {
return false;
}
bool NativeFileSystemDirectoryAccessConfirmationView::Accept() {
std::move(callback_).Run(PermissionAction::GRANTED);
return true;
}
bool NativeFileSystemDirectoryAccessConfirmationView::Cancel() {
std::move(callback_).Run(PermissionAction::DISMISSED);
return true;
}
gfx::Size
NativeFileSystemDirectoryAccessConfirmationView::CalculatePreferredSize()
const {
const int width = ChromeLayoutProvider::Get()->GetDistanceMetric(
DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH) -
margins().width();
return gfx::Size(width, GetHeightForWidth(width));
}
ui::ModalType NativeFileSystemDirectoryAccessConfirmationView::GetModalType()
const {
return ui::MODAL_TYPE_CHILD;
}
NativeFileSystemDirectoryAccessConfirmationView::
NativeFileSystemDirectoryAccessConfirmationView(
const url::Origin& origin,
const base::FilePath& path,
base::OnceCallback<void(PermissionAction result)> callback)
: callback_(std::move(callback)) {
const views::LayoutProvider* provider = ChromeLayoutProvider::Get();
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical,
provider->GetDialogInsetsForContentType(views::TEXT, views::TEXT),
provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)));
base::string16 formatted_origin =
url_formatter::FormatOriginForSecurityDisplay(
origin, url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
size_t offset;
auto label = std::make_unique<views::StyledLabel>(
l10n_util::GetStringFUTF16(
IDS_NATIVE_FILE_SYSTEM_DIRECTORY_ACCESS_CONFIRMATION_TEXT,
formatted_origin, &offset),
nullptr);
label->SetTextContext(CONTEXT_BODY_TEXT_SMALL);
label->SetDefaultTextStyle(STYLE_SECONDARY);
label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
views::StyledLabel::RangeStyleInfo origin_style;
origin_style.text_style = STYLE_EMPHASIZED_SECONDARY;
label->AddStyleRange(gfx::Range(offset, offset + formatted_origin.length()),
origin_style);
AddChildView(std::move(label));
auto file_label_container = std::make_unique<views::View>();
int indent =
provider->GetDistanceMetric(DISTANCE_SUBSECTION_HORIZONTAL_INDENT);
file_label_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal,
gfx::Insets(/*vertical=*/0, indent),
provider->GetDistanceMetric(views::DISTANCE_RELATED_LABEL_HORIZONTAL)));
auto icon = std::make_unique<views::ImageView>();
const SkColor icon_color = icon->GetNativeTheme()->GetSystemColor(
ui::NativeTheme::kColorId_DefaultIconColor);
icon->SetImage(gfx::CreateVectorIcon(vector_icons::kFolderOpenIcon,
/*dip_size=*/18, icon_color));
file_label_container->AddChildView(std::move(icon));
auto file_label = std::make_unique<views::Label>(path.LossyDisplayName(),
CONTEXT_BODY_TEXT_SMALL,
STYLE_EMPHASIZED_SECONDARY);
file_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
file_label_container->AddChildView(std::move(file_label));
AddChildView(std::move(file_label_container));
}
void ShowNativeFileSystemDirectoryAccessConfirmationDialog(
const url::Origin& origin,
const base::FilePath& path,
base::OnceCallback<void(PermissionAction result)> callback,
content::WebContents* web_contents) {
NativeFileSystemDirectoryAccessConfirmationView::ShowDialog(
origin, path, std::move(callback), web_contents);
}
// Copyright 2019 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.
#ifndef CHROME_BROWSER_UI_VIEWS_NATIVE_FILE_SYSTEM_NATIVE_FILE_SYSTEM_DIRECTORY_ACCESS_CONFIRMATION_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_NATIVE_FILE_SYSTEM_NATIVE_FILE_SYSTEM_DIRECTORY_ACCESS_CONFIRMATION_VIEW_H_
#include "base/macros.h"
#include "base/strings/string16.h"
#include "chrome/browser/permissions/permission_util.h"
#include "ui/views/window/dialog_delegate.h"
namespace base {
class FilePath;
}
namespace content {
class WebContents;
} // namespace content
namespace url {
class Origin;
} // namespace url
namespace views {
class Widget;
} // namespace views
// A dialog similar to FolderUploadConfirmationView that confirms that the user
// intended to give access to the specific folder.
// This is also a security measure against sites that trick a user into pressing
// enter, which would instantly confirm the OS folder picker and share the
// default folder selection without explicit user approval.
class NativeFileSystemDirectoryAccessConfirmationView
: public views::DialogDelegateView {
public:
~NativeFileSystemDirectoryAccessConfirmationView() override;
static views::Widget* ShowDialog(
const url::Origin& origin,
const base::FilePath& path,
base::OnceCallback<void(PermissionAction result)> callback,
content::WebContents* web_contents);
// views::DialogDelegateView:
base::string16 GetWindowTitle() const override;
int GetDefaultDialogButton() const override;
base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
bool ShouldShowCloseButton() const override;
bool Accept() override;
bool Cancel() override;
gfx::Size CalculatePreferredSize() const override;
ui::ModalType GetModalType() const override;
private:
NativeFileSystemDirectoryAccessConfirmationView(
const url::Origin& origin,
const base::FilePath& path,
base::OnceCallback<void(PermissionAction result)> callback);
base::OnceCallback<void(PermissionAction result)> callback_;
DISALLOW_COPY_AND_ASSIGN(NativeFileSystemDirectoryAccessConfirmationView);
};
#endif // CHROME_BROWSER_UI_VIEWS_NATIVE_FILE_SYSTEM_NATIVE_FILE_SYSTEM_DIRECTORY_ACCESS_CONFIRMATION_VIEW_H_
// Copyright 2019 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 "chrome/browser/ui/views/native_file_system/native_file_system_restricted_directory_dialog_view.h"
#include "base/files/file_path.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/test/test_browser_dialog.h"
#include "chrome/grit/generated_resources.h"
#include "ui/base/resource/resource_bundle.h"
class NativeFileSystemRestrictedDirectoryDialogViewTest
: public DialogBrowserTest {
public:
void SetUpOnMainThread() override {
// Release builds may strip out unused string resources when
// enable_resource_whitelist_generation is enabled. Manually override the
// strings needed by the dialog to ensure they are available for tests.
// TODO(https://crbug.com/979659): Remove these overrides once the strings
// are referenced from the Chrome binary.
auto& shared_resource_bundle = ui::ResourceBundle::GetSharedInstance();
shared_resource_bundle.OverrideLocaleStringResource(
IDS_NATIVE_FILE_SYSTEM_RESTRICTED_DIRECTORY_TITLE,
base::ASCIIToUTF16("Can't save to this folder"));
shared_resource_bundle.OverrideLocaleStringResource(
IDS_NATIVE_FILE_SYSTEM_RESTRICTED_DIRECTORY_TEXT,
base::ASCIIToUTF16("$1 can't save your changes to this folder because "
"it contains system files."));
shared_resource_bundle.OverrideLocaleStringResource(
IDS_NATIVE_FILE_SYSTEM_RESTRICTED_DIRECTORY_BUTTON,
base::ASCIIToUTF16("Choose a different folder"));
}
// DialogBrowserTest:
void ShowUi(const std::string& name) override {
NativeFileSystemRestrictedDirectoryDialogView::ShowDialog(
kTestOrigin, base::FilePath(FILE_PATH_LITERAL("/foo/bar")),
base::DoNothing(),
browser()->tab_strip_model()->GetActiveWebContents());
}
protected:
const url::Origin kTestOrigin =
url::Origin::Create(GURL("https://example.com"));
};
IN_PROC_BROWSER_TEST_F(NativeFileSystemRestrictedDirectoryDialogViewTest,
InvokeUi_default) {
ShowAndVerifyUi();
}
...@@ -2,15 +2,18 @@ ...@@ -2,15 +2,18 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "chrome/browser/ui/views/native_file_system/native_file_system_restricted_directory_dialog_view.h" #include "chrome/browser/ui/views/native_file_system/native_file_system_directory_access_confirmation_view.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/test/bind_test_util.h"
#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/test/test_browser_dialog.h" #include "chrome/browser/ui/test/test_browser_dialog.h"
#include "chrome/grit/generated_resources.h" #include "chrome/grit/generated_resources.h"
#include "ui/base/resource/resource_bundle.h" #include "ui/base/resource/resource_bundle.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/window/dialog_client_view.h"
class NativeFileSystemRestrictedDirectoryDialogViewTest class NativeFileSystemDirectoryAccessConfirmationViewTest
: public DialogBrowserTest { : public DialogBrowserTest {
public: public:
void SetUpOnMainThread() override { void SetUpOnMainThread() override {
...@@ -21,31 +24,73 @@ class NativeFileSystemRestrictedDirectoryDialogViewTest ...@@ -21,31 +24,73 @@ class NativeFileSystemRestrictedDirectoryDialogViewTest
// are referenced from the Chrome binary. // are referenced from the Chrome binary.
auto& shared_resource_bundle = ui::ResourceBundle::GetSharedInstance(); auto& shared_resource_bundle = ui::ResourceBundle::GetSharedInstance();
shared_resource_bundle.OverrideLocaleStringResource( shared_resource_bundle.OverrideLocaleStringResource(
IDS_NATIVE_FILE_SYSTEM_RESTRICTED_DIRECTORY_TITLE, IDS_NATIVE_FILE_SYSTEM_DIRECTORY_ACCESS_CONFIRMATION_TITLE,
base::ASCIIToUTF16("Can't save to this folder")); base::ASCIIToUTF16("Let site read this folder?"));
shared_resource_bundle.OverrideLocaleStringResource( shared_resource_bundle.OverrideLocaleStringResource(
IDS_NATIVE_FILE_SYSTEM_RESTRICTED_DIRECTORY_TEXT, IDS_NATIVE_FILE_SYSTEM_DIRECTORY_ACCESS_CONFIRMATION_TEXT,
base::ASCIIToUTF16("$1 can't save your changes to this folder because " base::ASCIIToUTF16("$1 will be able to read all files in the following "
"it contains system files.")); "folder. This site can see any changes to the "
shared_resource_bundle.OverrideLocaleStringResource( "folder only while this tab is open."));
IDS_NATIVE_FILE_SYSTEM_RESTRICTED_DIRECTORY_BUTTON,
base::ASCIIToUTF16("Choose a different folder"));
} }
// DialogBrowserTest: // DialogBrowserTest:
void ShowUi(const std::string& name) override { void ShowUi(const std::string& name) override {
NativeFileSystemRestrictedDirectoryDialogView::ShowDialog( widget_ = NativeFileSystemDirectoryAccessConfirmationView::ShowDialog(
kTestOrigin, base::FilePath(FILE_PATH_LITERAL("/foo/bar")), kTestOrigin, base::FilePath(FILE_PATH_LITERAL("/foo/bar")),
base::DoNothing(), base::BindLambdaForTesting([&](PermissionAction result) {
callback_called_ = true;
callback_result_ = result;
}),
browser()->tab_strip_model()->GetActiveWebContents()); browser()->tab_strip_model()->GetActiveWebContents());
} }
protected: protected:
const url::Origin kTestOrigin = const url::Origin kTestOrigin =
url::Origin::Create(GURL("https://example.com")); url::Origin::Create(GURL("https://example.com"));
views::Widget* widget_ = nullptr;
bool callback_called_ = false;
PermissionAction callback_result_ = PermissionAction::IGNORED;
}; };
IN_PROC_BROWSER_TEST_F(NativeFileSystemRestrictedDirectoryDialogViewTest, IN_PROC_BROWSER_TEST_F(NativeFileSystemDirectoryAccessConfirmationViewTest,
AcceptIsntDefaultFocused) {
ShowUi(std::string());
EXPECT_NE(widget_->client_view()->AsDialogClientView()->ok_button(),
widget_->GetFocusManager()->GetFocusedView());
widget_->Close();
base::RunLoop().RunUntilIdle();
}
IN_PROC_BROWSER_TEST_F(NativeFileSystemDirectoryAccessConfirmationViewTest,
AcceptRunsCallback) {
ShowUi(std::string());
widget_->client_view()->AsDialogClientView()->AcceptWindow();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(PermissionAction::GRANTED, callback_result_);
base::RunLoop().RunUntilIdle();
}
IN_PROC_BROWSER_TEST_F(NativeFileSystemDirectoryAccessConfirmationViewTest,
CancelRunsCallback) {
ShowUi(std::string());
widget_->client_view()->AsDialogClientView()->CancelWindow();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(PermissionAction::DISMISSED, callback_result_);
base::RunLoop().RunUntilIdle();
}
IN_PROC_BROWSER_TEST_F(NativeFileSystemDirectoryAccessConfirmationViewTest,
CancelsWhenClosed) {
ShowUi(std::string());
widget_->Close();
EXPECT_TRUE(callback_called_);
EXPECT_EQ(PermissionAction::DISMISSED, callback_result_);
base::RunLoop().RunUntilIdle();
}
IN_PROC_BROWSER_TEST_F(NativeFileSystemDirectoryAccessConfirmationViewTest,
InvokeUi_default) { InvokeUi_default) {
ShowAndVerifyUi(); ShowAndVerifyUi();
} }
...@@ -1807,6 +1807,7 @@ if (!is_android) { ...@@ -1807,6 +1807,7 @@ if (!is_android) {
"../browser/ui/views/media_router/media_router_dialog_controller_views_browsertest.cc", "../browser/ui/views/media_router/media_router_dialog_controller_views_browsertest.cc",
"../browser/ui/views/media_router/media_router_ui_browsertest.cc", "../browser/ui/views/media_router/media_router_ui_browsertest.cc",
"../browser/ui/views/media_router/presentation_receiver_window_view_browsertest.cc", "../browser/ui/views/media_router/presentation_receiver_window_view_browsertest.cc",
"../browser/ui/views/native_file_system/native_file_system_directory_access_confirmation_view_browsertest.cc",
"../browser/ui/views/native_file_system/native_file_system_permission_view_browsertest.cc", "../browser/ui/views/native_file_system/native_file_system_permission_view_browsertest.cc",
"../browser/ui/views/native_file_system/native_file_system_restricted_directory_dialog_view_browsertest.cc", "../browser/ui/views/native_file_system/native_file_system_restricted_directory_dialog_view_browsertest.cc",
"../browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc", "../browser/ui/views/omnibox/omnibox_popup_contents_view_browsertest.cc",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment