Commit 100a68cd authored by Roger Tawa's avatar Roger Tawa Committed by Commit Bot

Hook drag+drop code to the upload content checks.

Adds checks (enabled via chrome policy) of data dragged and dropped into web
pages to make sure it complies with corporate policies before allowing the page
to access it.  When checks are not enabled, or for non enterprise users, data
dropped into a page is always considered accessible.

Bug: 999149
Change-Id: I27c1ee5f2f7e5a3054521d6353521965e5f6c221
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1814619
Commit-Queue: Roger Tawa <rogerta@chromium.org>
Reviewed-by: default avatarAlex Moshchuk <alexmos@chromium.org>
Reviewed-by: default avatarSadrul Chowdhury <sadrul@chromium.org>
Reviewed-by: default avatarPeter Kasting <pkasting@chromium.org>
Cr-Commit-Position: refs/heads/master@{#701390}
parent 8be89ae0
...@@ -1997,6 +1997,8 @@ jumbo_split_static_library("ui") { ...@@ -1997,6 +1997,8 @@ jumbo_split_static_library("ui") {
"autofill/payments/webauthn_offer_dialog_view.h", "autofill/payments/webauthn_offer_dialog_view.h",
"frame/window_frame_util.cc", "frame/window_frame_util.cc",
"frame/window_frame_util.h", "frame/window_frame_util.h",
"tab_contents/chrome_web_contents_view_handle_drop.cc",
"tab_contents/chrome_web_contents_view_handle_drop.h",
"views/autofill/payments/webauthn_offer_dialog_view_impl.cc", "views/autofill/payments/webauthn_offer_dialog_view_impl.cc",
"views/autofill/payments/webauthn_offer_dialog_view_impl.h", "views/autofill/payments/webauthn_offer_dialog_view_impl.h",
"views/close_bubble_on_tab_activation_helper.cc", "views/close_bubble_on_tab_activation_helper.cc",
......
// 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/tab_contents/chrome_web_contents_view_handle_drop.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view_delegate.h"
#include "content/public/common/drop_data.h"
namespace {
void DeepScanCompletionCallback(
content::WebContentsViewDelegate::DropCompletionCallback callback,
const safe_browsing::DeepScanningDialogDelegate::Data& data,
const safe_browsing::DeepScanningDialogDelegate::Result& result) {
// If any result is negative, block the drop.
const auto all_true_fn = [](const auto& vec) {
return std::all_of(vec.cbegin(), vec.cend(), [](bool b) { return b; });
};
bool all_true =
all_true_fn(result.text_results) && all_true_fn(result.paths_results);
std::move(callback).Run(
all_true
? content::WebContentsViewDelegate::DropCompletionResult::kContinue
: content::WebContentsViewDelegate::DropCompletionResult::kAbort);
}
} // namespace
void HandleOnPerformDrop(
content::WebContents* web_contents,
const content::DropData& drop_data,
content::WebContentsViewDelegate::DropCompletionCallback callback) {
safe_browsing::DeepScanningDialogDelegate::Data data;
Profile* profile =
Profile::FromBrowserContext(web_contents->GetBrowserContext());
if (!safe_browsing::DeepScanningDialogDelegate::IsEnabled(
profile, web_contents->GetLastCommittedURL(), &data)) {
std::move(callback).Run(
content::WebContentsViewDelegate::DropCompletionResult::kContinue);
return;
}
// Collect the data that needs to be scanned.
if (!drop_data.url_title.empty())
data.text.push_back(drop_data.url_title);
if (!drop_data.text.is_null())
data.text.push_back(drop_data.text.string());
if (!drop_data.html.is_null())
data.text.push_back(drop_data.html.string());
if (!drop_data.file_contents.empty())
data.text.push_back(base::UTF8ToUTF16(drop_data.file_contents));
for (const auto& file : drop_data.filenames)
data.paths.push_back(file.path);
// TODO(crbug.com/1008040): how to handle drop_data.file_system_files?
// These are URLs that use the filesystem: schema. Support for this API
// is unclear.
safe_browsing::DeepScanningDialogDelegate::ShowForWebContents(
web_contents, std::move(data),
base::BindOnce(&DeepScanCompletionCallback, std::move(callback)));
}
// 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_TAB_CONTENTS_CHROME_WEB_CONTENTS_VIEW_HANDLE_DROP_H_
#define CHROME_BROWSER_UI_TAB_CONTENTS_CHROME_WEB_CONTENTS_VIEW_HANDLE_DROP_H_
#include "content/public/browser/web_contents_view_delegate.h"
namespace content {
class WebContents;
struct DropData;
} // namespace content
// Common code to be called from the implementation of
// WebContentsViewDelegate::OnPerformDrop() for each platform.
void HandleOnPerformDrop(
content::WebContents* web_contents,
const content::DropData& drop_data,
content::WebContentsViewDelegate::DropCompletionCallback callback);
#endif // CHROME_BROWSER_UI_TAB_CONTENTS_CHROME_WEB_CONTENTS_VIEW_HANDLE_DROP_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 <memory>
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/run_loop.h"
#include "base/strings/nullable_string16.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_dialog_delegate.h"
#include "chrome/browser/safe_browsing/cloud_content_scanning/fake_deep_scanning_dialog_delegate.h"
#include "chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/safe_browsing/common/safe_browsing_prefs.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/drop_data.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
class ChromeWebContentsViewDelegateHandleOnPerformDrop : public testing::Test {
public:
ChromeWebContentsViewDelegateHandleOnPerformDrop() {
EXPECT_TRUE(profile_manager_.SetUp());
profile_ = profile_manager_.CreateTestingProfile("test-user");
}
void RunUntilDone() { run_loop_->Run(); }
content::WebContents* contents() {
if (!web_contents_) {
content::WebContents::CreateParams params(profile_);
web_contents_ = content::WebContents::Create(params);
}
return web_contents_.get();
}
void EnableDeepScanning(bool enable, bool scan_succeeds) {
SetScanPolicies(enable ? safe_browsing::CHECK_UPLOADS
: safe_browsing::CHECK_NONE);
if (!enable)
return;
run_loop_.reset(new base::RunLoop());
safe_browsing::DeepScanningDialogDelegate::SetDMTokenForTesting("dm_token");
safe_browsing::DeepScanningDialogDelegate::SetFactoryForTesting(
base::BindRepeating(
&safe_browsing::FakeDeepScanningDialogDelegate::Create,
run_loop_->QuitClosure(),
base::Bind([](bool scan_succeeds,
const base::FilePath&) { return scan_succeeds; },
scan_succeeds),
"dm_token"));
}
// Common code for running the test cases.
void RunTest(const content::DropData& data,
bool enable,
bool scan_succeeds = false) {
EnableDeepScanning(enable, scan_succeeds);
content::WebContentsViewDelegate::DropCompletionResult result =
scan_succeeds
? content::WebContentsViewDelegate::DropCompletionResult::kContinue
: content::WebContentsViewDelegate::DropCompletionResult::kAbort;
bool called = false;
HandleOnPerformDrop(
contents(), data,
base::Bind(
[](content::WebContentsViewDelegate::DropCompletionResult
expected_result,
bool* called,
content::WebContentsViewDelegate::DropCompletionResult result) {
EXPECT_EQ(expected_result, result);
*called = true;
},
result, &called));
if (enable)
RunUntilDone();
EXPECT_TRUE(called);
}
private:
void SetScanPolicies(safe_browsing::CheckContentComplianceValues state) {
PrefService* pref_service =
TestingBrowserProcess::GetGlobal()->local_state();
pref_service->SetInteger(prefs::kCheckContentCompliance, state);
pref_service->SetInteger(prefs::kDelayDeliveryUntilVerdict,
safe_browsing::DELAY_UPLOADS);
}
content::BrowserTaskEnvironment task_environment_;
TestingProfileManager profile_manager_{TestingBrowserProcess::GetGlobal()};
TestingProfile* profile_;
std::unique_ptr<base::RunLoop> run_loop_;
std::unique_ptr<content::WebContents> web_contents_;
};
// When no drop data is specified, HandleOnPerformDrop() should indicate
// the caller can proceed, whether scanning is enabled or not.
TEST_F(ChromeWebContentsViewDelegateHandleOnPerformDrop, NoData) {
content::DropData data;
RunTest(data, /*enable=*/false, /*scan_succeeds=*/true);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/true);
}
// Make sure DropData::url_title is handled correctly.
TEST_F(ChromeWebContentsViewDelegateHandleOnPerformDrop, UrlTitle) {
content::DropData data;
data.url_title = base::UTF8ToUTF16("title");
RunTest(data, /*enable=*/false, /*scan_succeeds=*/true);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/false);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/true);
}
// Make sure DropData::text is handled correctly.
TEST_F(ChromeWebContentsViewDelegateHandleOnPerformDrop, Text) {
content::DropData data;
data.text = base::NullableString16(base::UTF8ToUTF16("text"), false);
RunTest(data, /*enable=*/false, /*scan_succeeds=*/true);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/false);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/true);
}
// Make sure DropData::html is handled correctly.
TEST_F(ChromeWebContentsViewDelegateHandleOnPerformDrop, Html) {
content::DropData data;
data.html = base::NullableString16(base::UTF8ToUTF16("<html></html>"), false);
RunTest(data, /*enable=*/false, /*scan_succeeds=*/true);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/false);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/true);
}
// Make sure DropData::file_contents is handled correctly.
TEST_F(ChromeWebContentsViewDelegateHandleOnPerformDrop, FileContents) {
content::DropData data;
data.file_contents = "file_contents";
RunTest(data, /*enable=*/false, /*scan_succeeds=*/true);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/false);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/true);
}
// Make sure DropData::filenames is handled correctly.
TEST_F(ChromeWebContentsViewDelegateHandleOnPerformDrop, Files) {
content::DropData data;
data.filenames.emplace_back(base::FilePath(FILE_PATH_LITERAL("C:\\Foo")),
base::FilePath(FILE_PATH_LITERAL("Foo")));
data.filenames.emplace_back(base::FilePath(FILE_PATH_LITERAL("C:\\Bar")),
base::FilePath(FILE_PATH_LITERAL("Bar")));
RunTest(data, /*enable=*/false, /*scan_succeeds=*/true);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/false);
RunTest(data, /*enable=*/true, /*scan_succeeds=*/true);
}
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
#include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/sad_tab_helper.h" #include "chrome/browser/ui/sad_tab_helper.h"
#include "chrome/browser/ui/tab_contents/chrome_web_contents_view_delegate.h" #include "chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop.h"
#include "chrome/browser/ui/views/renderer_context_menu/render_view_context_menu_views.h" #include "chrome/browser/ui/views/renderer_context_menu/render_view_context_menu_views.h"
#include "chrome/browser/ui/views/sad_tab_view.h" #include "chrome/browser/ui/views/sad_tab_view.h"
#include "chrome/browser/ui/views/tab_contents/chrome_web_contents_view_focus_helper.h" #include "chrome/browser/ui/views/tab_contents/chrome_web_contents_view_focus_helper.h"
...@@ -103,6 +103,12 @@ void ChromeWebContentsViewDelegateViews::ShowContextMenu( ...@@ -103,6 +103,12 @@ void ChromeWebContentsViewDelegateViews::ShowContextMenu(
params)); params));
} }
void ChromeWebContentsViewDelegateViews::OnPerformDrop(
const content::DropData& drop_data,
DropCompletionCallback callback) {
HandleOnPerformDrop(web_contents_, drop_data, std::move(callback));
}
content::WebContentsViewDelegate* CreateWebContentsViewDelegate( content::WebContentsViewDelegate* CreateWebContentsViewDelegate(
content::WebContents* web_contents) { content::WebContents* web_contents) {
return new ChromeWebContentsViewDelegateViews(web_contents); return new ChromeWebContentsViewDelegateViews(web_contents);
......
...@@ -41,6 +41,8 @@ class ChromeWebContentsViewDelegateViews ...@@ -41,6 +41,8 @@ class ChromeWebContentsViewDelegateViews
bool TakeFocus(bool reverse) override; bool TakeFocus(bool reverse) override;
void ShowContextMenu(content::RenderFrameHost* render_frame_host, void ShowContextMenu(content::RenderFrameHost* render_frame_host,
const content::ContextMenuParams& params) override; const content::ContextMenuParams& params) override;
void OnPerformDrop(const content::DropData& drop_data,
DropCompletionCallback callback) override;
// Overridden from ContextMenuDelegate. // Overridden from ContextMenuDelegate.
std::unique_ptr<RenderViewContextMenuBase> BuildMenu( std::unique_ptr<RenderViewContextMenuBase> BuildMenu(
......
...@@ -23,6 +23,8 @@ class ChromeWebContentsViewDelegateViewsMac ...@@ -23,6 +23,8 @@ class ChromeWebContentsViewDelegateViewsMac
void ResetStoredFocus() override; void ResetStoredFocus() override;
bool Focus() override; bool Focus() override;
bool TakeFocus(bool reverse) override; bool TakeFocus(bool reverse) override;
void OnPerformDrop(const content::DropData& drop_data,
DropCompletionCallback callback) override;
private: private:
content::WebContents* web_contents_; content::WebContents* web_contents_;
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#import "chrome/browser/ui/views/tab_contents/chrome_web_contents_view_delegate_views_mac.h" #import "chrome/browser/ui/views/tab_contents/chrome_web_contents_view_delegate_views_mac.h"
#include "chrome/browser/ui/sad_tab_helper.h" #include "chrome/browser/ui/sad_tab_helper.h"
#include "chrome/browser/ui/tab_contents/chrome_web_contents_view_handle_drop.h"
#include "chrome/browser/ui/views/sad_tab_view.h" #include "chrome/browser/ui/views/sad_tab_view.h"
#include "chrome/browser/ui/views/tab_contents/chrome_web_contents_view_focus_helper.h" #include "chrome/browser/ui/views/tab_contents/chrome_web_contents_view_focus_helper.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
...@@ -49,6 +50,12 @@ bool ChromeWebContentsViewDelegateViewsMac::TakeFocus(bool reverse) { ...@@ -49,6 +50,12 @@ bool ChromeWebContentsViewDelegateViewsMac::TakeFocus(bool reverse) {
return GetFocusHelper()->TakeFocus(reverse); return GetFocusHelper()->TakeFocus(reverse);
} }
void ChromeWebContentsViewDelegateViewsMac::OnPerformDrop(
const content::DropData& drop_data,
DropCompletionCallback callback) {
HandleOnPerformDrop(web_contents_, drop_data, std::move(callback));
}
content::WebContentsViewDelegate* CreateWebContentsViewDelegate( content::WebContentsViewDelegate* CreateWebContentsViewDelegate(
content::WebContents* web_contents) { content::WebContents* web_contents) {
return new ChromeWebContentsViewDelegateViewsMac(web_contents); return new ChromeWebContentsViewDelegateViewsMac(web_contents);
......
...@@ -3860,6 +3860,7 @@ test("unit_tests") { ...@@ -3860,6 +3860,7 @@ test("unit_tests") {
"../browser/ui/search/search_tab_helper_unittest.cc", "../browser/ui/search/search_tab_helper_unittest.cc",
"../browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model_unittest.cc", "../browser/ui/send_tab_to_self/send_tab_to_self_sub_menu_model_unittest.cc",
"../browser/ui/serial/serial_chooser_controller_unittest.cc", "../browser/ui/serial/serial_chooser_controller_unittest.cc",
"../browser/ui/tab_contents/chrome_web_contents_view_handle_drop_unittest.cc",
"../browser/ui/tab_contents/tab_contents_iterator_unittest.cc", "../browser/ui/tab_contents/tab_contents_iterator_unittest.cc",
"../browser/ui/tabs/pinned_tab_codec_unittest.cc", "../browser/ui/tabs/pinned_tab_codec_unittest.cc",
"../browser/ui/tabs/pinned_tab_service_unittest.cc", "../browser/ui/tabs/pinned_tab_service_unittest.cc",
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/command_line.h" #include "base/command_line.h"
#include "base/containers/flat_set.h" #include "base/containers/flat_set.h"
#include "base/feature_list.h" #include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/message_loop/message_loop_current.h" #include "base/message_loop/message_loop_current.h"
...@@ -52,7 +53,6 @@ ...@@ -52,7 +53,6 @@
#include "content/public/common/child_process_host.h" #include "content/public/common/child_process_host.h"
#include "content/public/common/content_client.h" #include "content/public/common/content_client.h"
#include "content/public/common/content_features.h" #include "content/public/common/content_features.h"
#include "content/public/common/drop_data.h"
#include "net/base/filename_util.h" #include "net/base/filename_util.h"
#include "third_party/blink/public/platform/web_input_event.h" #include "third_party/blink/public/platform/web_input_event.h"
#include "ui/aura/client/aura_constants.h" #include "ui/aura/client/aura_constants.h"
...@@ -399,6 +399,25 @@ aura::Window* GetHostWindow(aura::Window* window) { ...@@ -399,6 +399,25 @@ aura::Window* GetHostWindow(aura::Window* window) {
} // namespace } // namespace
WebContentsViewAura::OnPerformDropContext::OnPerformDropContext(
RenderWidgetHostImpl* target_rwh,
const ui::DropTargetEvent& event,
std::unique_ptr<ui::OSExchangeData> data,
base::ScopedClosureRunner end_drag_runner,
base::Optional<gfx::PointF> transformed_pt,
gfx::PointF screen_pt)
: target_rwh(target_rwh->GetWeakPtr()),
event(event),
data(std::move(data)),
end_drag_runner(std::move(end_drag_runner)),
transformed_pt(std::move(transformed_pt)),
screen_pt(screen_pt) {}
WebContentsViewAura::OnPerformDropContext::OnPerformDropContext(
OnPerformDropContext&&) = default;
WebContentsViewAura::OnPerformDropContext::~OnPerformDropContext() = default;
#if defined(OS_WIN) #if defined(OS_WIN)
// A web contents observer that watches for navigations while an async drop // A web contents observer that watches for navigations while an async drop
// operation is in progress during virtual file data retrieval and temp file // operation is in progress during virtual file data retrieval and temp file
...@@ -1424,10 +1443,44 @@ void WebContentsViewAura::PerformDropCallback( ...@@ -1424,10 +1443,44 @@ void WebContentsViewAura::PerformDropCallback(
if (!current_drop_data_) if (!current_drop_data_)
return; return;
const int key_modifiers = ui::EventFlagsToWebEventModifiers(event.flags()); OnPerformDropContext context(target_rwh, event, std::move(data),
std::move(end_drag_runner), transformed_pt,
screen_pt);
// |delegate_| may be null in unit tests.
if (delegate_) {
delegate_->OnPerformDrop(
*current_drop_data_,
base::BindOnce(&WebContentsViewAura::FinishOnPerformDropCallback,
weak_ptr_factory_.GetWeakPtr(), std::move(context)));
} else {
FinishOnPerformDropCallback(
std::move(context),
WebContentsViewDelegate::DropCompletionResult::kContinue);
}
}
void WebContentsViewAura::FinishOnPerformDropCallback(
OnPerformDropContext context,
WebContentsViewDelegate::DropCompletionResult result) {
const int key_modifiers =
ui::EventFlagsToWebEventModifiers(context.event.flags());
// This is possibly an async callback. Make sure the RWH is still valid.
if (!context.target_rwh || !IsValidDragTarget(context.target_rwh.get()))
return;
if (result != WebContentsViewDelegate::DropCompletionResult::kContinue) {
if (!drop_callback_for_testing_.is_null()) {
std::move(drop_callback_for_testing_)
.Run(context.target_rwh.get(), *current_drop_data_,
context.transformed_pt.value(), context.screen_pt, key_modifiers,
/*drop_allowed=*/false);
}
return;
}
#if defined(OS_WIN) #if defined(OS_WIN)
if (ShouldIncludeVirtualFiles(*current_drop_data_) && if (ShouldIncludeVirtualFiles(*current_drop_data_) &&
data->HasVirtualFilenames()) { context.data->HasVirtualFilenames()) {
// Asynchronously retrieve the actual content of any virtual files now (this // Asynchronously retrieve the actual content of any virtual files now (this
// step is not needed for "real" files already on the file system, e.g. // step is not needed for "real" files already on the file system, e.g.
// those dropped on Chromium from the desktop). When all content has been // those dropped on Chromium from the desktop). When all content has been
...@@ -1441,22 +1494,23 @@ void WebContentsViewAura::PerformDropCallback( ...@@ -1441,22 +1494,23 @@ void WebContentsViewAura::PerformDropCallback(
// GetVirtualFilesAsTempFiles will immediately return false if there are no // GetVirtualFilesAsTempFiles will immediately return false if there are no
// virtual files to retrieve (all items are folders e.g.) and no callback // virtual files to retrieve (all items are folders e.g.) and no callback
// will be received. // will be received.
if (data->GetVirtualFilesAsTempFiles(std::move(callback))) { if (context.data->GetVirtualFilesAsTempFiles(std::move(callback))) {
// Cache the parameters as they were at the time of the drop. This is // Cache the parameters as they were at the time of the drop. This is
// needed for checking that the drop target is still valid when the async // needed for checking that the drop target is still valid when the async
// operation completes. // operation completes.
async_drop_navigation_observer_ = async_drop_navigation_observer_ =
std::make_unique<AsyncDropNavigationObserver>( std::make_unique<AsyncDropNavigationObserver>(
web_contents_, std::move(current_drop_data_), web_contents_, std::move(current_drop_data_),
std::move(end_drag_runner), target_rwh, transformed_pt.value(), std::move(context.end_drag_runner), context.target_rwh.get(),
screen_pt, key_modifiers); context.transformed_pt.value(), context.screen_pt, key_modifiers);
return; return;
} }
} }
#endif #endif
CompleteDrop(target_rwh, *current_drop_data_, transformed_pt.value(), CompleteDrop(context.target_rwh.get(), *current_drop_data_,
screen_pt, key_modifiers); context.transformed_pt.value(), context.screen_pt,
key_modifiers);
current_drop_data_.reset(); current_drop_data_.reset();
} }
...@@ -1485,7 +1539,7 @@ void WebContentsViewAura::CompleteDrop(RenderWidgetHostImpl* target_rwh, ...@@ -1485,7 +1539,7 @@ void WebContentsViewAura::CompleteDrop(RenderWidgetHostImpl* target_rwh,
if (!drop_callback_for_testing_.is_null()) { if (!drop_callback_for_testing_.is_null()) {
std::move(drop_callback_for_testing_) std::move(drop_callback_for_testing_)
.Run(target_rwh, drop_data, client_pt, screen_pt, key_modifiers, .Run(target_rwh, drop_data, client_pt, screen_pt, key_modifiers,
/*drop_allowed*/ true); /*drop_allowed=*/true);
} }
} }
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "content/browser/renderer_host/render_view_host_delegate_view.h" #include "content/browser/renderer_host/render_view_host_delegate_view.h"
#include "content/browser/web_contents/web_contents_view.h" #include "content/browser/web_contents/web_contents_view.h"
...@@ -21,9 +22,12 @@ ...@@ -21,9 +22,12 @@
#include "content/common/content_export.h" #include "content/common/content_export.h"
#include "content/public/browser/global_routing_id.h" #include "content/public/browser/global_routing_id.h"
#include "content/public/browser/visibility.h" #include "content/public/browser/visibility.h"
#include "content/public/browser/web_contents_view_delegate.h"
#include "content/public/common/drop_data.h"
#include "ui/aura/client/drag_drop_delegate.h" #include "ui/aura/client/drag_drop_delegate.h"
#include "ui/aura/window.h" #include "ui/aura/window.h"
#include "ui/aura/window_delegate.h" #include "ui/aura/window_delegate.h"
#include "ui/base/dragdrop/drop_target_event.h"
namespace ui { namespace ui {
class DropTargetEvent; class DropTargetEvent;
...@@ -35,7 +39,6 @@ class GestureNavSimple; ...@@ -35,7 +39,6 @@ class GestureNavSimple;
class RenderWidgetHostImpl; class RenderWidgetHostImpl;
class RenderWidgetHostViewAura; class RenderWidgetHostViewAura;
class TouchSelectionControllerClientAura; class TouchSelectionControllerClientAura;
class WebContentsViewDelegate;
class WebContentsImpl; class WebContentsImpl;
class WebDragDestDelegate; class WebDragDestDelegate;
...@@ -65,6 +68,27 @@ class CONTENT_EXPORT WebContentsViewAura ...@@ -65,6 +68,27 @@ class CONTENT_EXPORT WebContentsViewAura
RenderWidgetHostViewCreateFunction create_render_widget_host_view); RenderWidgetHostViewCreateFunction create_render_widget_host_view);
private: private:
// A structure used to keep drop context for asynchronously finishing a
// drop operation. This is required because some drop event data gets
// cleared out once PerformDropCallback() returns.
struct CONTENT_EXPORT OnPerformDropContext {
OnPerformDropContext(RenderWidgetHostImpl* target_rwh,
const ui::DropTargetEvent& event,
std::unique_ptr<ui::OSExchangeData> data,
base::ScopedClosureRunner end_drag_runner,
base::Optional<gfx::PointF> transformed_pt,
gfx::PointF screen_pt);
OnPerformDropContext(OnPerformDropContext&& other);
~OnPerformDropContext();
base::WeakPtr<RenderWidgetHostImpl> target_rwh;
ui::DropTargetEvent event;
std::unique_ptr<ui::OSExchangeData> data;
base::ScopedClosureRunner end_drag_runner;
base::Optional<gfx::PointF> transformed_pt;
gfx::PointF screen_pt;
};
friend class WebContentsViewAuraTest; friend class WebContentsViewAuraTest;
FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, EnableDisableOverscroll); FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, EnableDisableOverscroll);
FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, DragDropFiles); FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, DragDropFiles);
...@@ -75,6 +99,8 @@ class CONTENT_EXPORT WebContentsViewAura ...@@ -75,6 +99,8 @@ class CONTENT_EXPORT WebContentsViewAura
DragDropVirtualFilesOriginateFromRenderer); DragDropVirtualFilesOriginateFromRenderer);
FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, DragDropUrlData); FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, DragDropUrlData);
FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, DragDropOnOopif); FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, DragDropOnOopif);
FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, OnPerformDrop_DeepScanOK);
FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest, OnPerformDrop_DeepScanBad);
class WindowObserver; class WindowObserver;
...@@ -207,6 +233,11 @@ class CONTENT_EXPORT WebContentsViewAura ...@@ -207,6 +233,11 @@ class CONTENT_EXPORT WebContentsViewAura
base::WeakPtr<RenderWidgetHostViewBase> target, base::WeakPtr<RenderWidgetHostViewBase> target,
base::Optional<gfx::PointF> transformed_pt); base::Optional<gfx::PointF> transformed_pt);
// Called from PerformDropCallback() to finish processing the drop.
void FinishOnPerformDropCallback(
OnPerformDropContext context,
WebContentsViewDelegate::DropCompletionResult result);
// Completes a drop operation by communicating the drop data to the renderer // Completes a drop operation by communicating the drop data to the renderer
// process. // process.
void CompleteDrop(RenderWidgetHostImpl* target_rwh, void CompleteDrop(RenderWidgetHostImpl* target_rwh,
...@@ -297,7 +328,7 @@ class CONTENT_EXPORT WebContentsViewAura ...@@ -297,7 +328,7 @@ class CONTENT_EXPORT WebContentsViewAura
bool init_rwhv_with_null_parent_for_testing_; bool init_rwhv_with_null_parent_for_testing_;
// Used to ensure the drag and drop callbacks bound to this // Used to ensure that the drag and drop callbacks bound to this
// object are canceled when this object is destroyed. // object are canceled when this object is destroyed.
base::WeakPtrFactory<WebContentsViewAura> weak_ptr_factory_{this}; base::WeakPtrFactory<WebContentsViewAura> weak_ptr_factory_{this};
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_widget_host.h" #include "content/public/browser/render_widget_host.h"
#include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_view_delegate.h"
#include "content/public/browser/web_drag_dest_delegate.h" #include "content/public/browser/web_drag_dest_delegate.h"
#include "content/public/common/content_switches.h" #include "content/public/common/content_switches.h"
#include "content/public/test/browser_test_utils.h" #include "content/public/test/browser_test_utils.h"
...@@ -54,6 +55,8 @@ ...@@ -54,6 +55,8 @@
#include "ui/events/event_utils.h" #include "ui/events/event_utils.h"
#include "ui/events/test/event_generator.h" #include "ui/events/test/event_generator.h"
namespace content {
namespace { namespace {
// TODO(tdresser): Find a way to avoid sleeping like this. See crbug.com/405282 // TODO(tdresser): Find a way to avoid sleeping like this. See crbug.com/405282
...@@ -66,10 +69,22 @@ void GiveItSomeTime() { ...@@ -66,10 +69,22 @@ void GiveItSomeTime() {
run_loop.Run(); run_loop.Run();
} }
} //namespace // A test delegate used in drag and drop tests to simulate either good or bad
// deep scans of data.
class TestWebContentsViewDelegate : public WebContentsViewDelegate {
public:
TestWebContentsViewDelegate(DropCompletionResult result) : result_(result) {}
void OnPerformDrop(const DropData& drop_data,
DropCompletionCallback callback) override {
std::move(callback).Run(result_);
}
namespace content { private:
DropCompletionResult result_;
};
} // namespace
class WebContentsViewAuraTest : public ContentBrowserTest { class WebContentsViewAuraTest : public ContentBrowserTest {
public: public:
...@@ -252,15 +267,19 @@ class WebContentsViewAuraTest : public ContentBrowserTest { ...@@ -252,15 +267,19 @@ class WebContentsViewAuraTest : public ContentBrowserTest {
} }
void OnDragOver() override {} void OnDragOver() override {}
void OnDragEnter() override {} void OnDragEnter() override {}
void OnDrop() override {} void OnDrop() override { on_drop_called_ = true; }
void OnDragLeave() override {} void OnDragLeave() override { on_drag_leave_called_ = true; }
void OnReceiveDragData(const ui::OSExchangeData& data) override {} void OnReceiveDragData(const ui::OSExchangeData& data) override {}
void Reset() { drag_initialize_called_ = false; } void Reset() { drag_initialize_called_ = false; }
bool GetDragInitializeCalled() { return drag_initialize_called_; } bool GetDragInitializeCalled() { return drag_initialize_called_; }
bool GetOnDropCalled() { return on_drop_called_; }
bool GetOnDragLeaveCalled() { return on_drag_leave_called_; }
private: private:
bool drag_initialize_called_ = false; bool drag_initialize_called_ = false;
bool on_drop_called_ = false;
bool on_drag_leave_called_ = false;
}; };
// ContentBrowserTest: // ContentBrowserTest:
...@@ -606,6 +625,72 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, DragDropOnOopif) { ...@@ -606,6 +625,72 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, DragDropOnOopif) {
} }
} }
IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, OnPerformDrop_DeepScanOK) {
StartTestWithPage("/simple_page.html");
WebContentsImpl* contents =
static_cast<WebContentsImpl*>(shell()->web_contents());
WebContentsViewAura* view =
static_cast<WebContentsViewAura*>(contents->GetView());
drag_dest_delegate_.Reset();
view->SetDragDestDelegateForTesting(&drag_dest_delegate_);
// The view takes ownership of the delegate. The delegate simulates that
// the scans passed.
view->SetDelegateForTesting(new TestWebContentsViewDelegate(
WebContentsViewDelegate::DropCompletionResult::kContinue));
std::unique_ptr<ui::OSExchangeData> data =
std::make_unique<ui::OSExchangeData>();
view->RegisterDropCallbackForTesting(base::BindOnce(
&WebContentsViewAuraTest::OnDropComplete, base::Unretained(this)));
base::RunLoop run_loop;
async_drop_closure_ = run_loop.QuitClosure();
gfx::PointF point = {10, 10};
ui::DropTargetEvent event(*data.get(), point, point,
ui::DragDropTypes::DRAG_COPY);
view->OnDragEntered(event);
EXPECT_TRUE(drag_dest_delegate_.GetDragInitializeCalled());
view->OnPerformDrop(event, std::move(data));
run_loop.Run();
EXPECT_TRUE(drag_dest_delegate_.GetOnDropCalled());
}
IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, OnPerformDrop_DeepScanBad) {
StartTestWithPage("/simple_page.html");
WebContentsImpl* contents =
static_cast<WebContentsImpl*>(shell()->web_contents());
WebContentsViewAura* view =
static_cast<WebContentsViewAura*>(contents->GetView());
drag_dest_delegate_.Reset();
view->SetDragDestDelegateForTesting(&drag_dest_delegate_);
// The view takes ownership of the delegate. The delegate simulates that
// the scans failed.
view->SetDelegateForTesting(new TestWebContentsViewDelegate(
WebContentsViewDelegate::DropCompletionResult::kAbort));
std::unique_ptr<ui::OSExchangeData> data =
std::make_unique<ui::OSExchangeData>();
view->RegisterDropCallbackForTesting(base::BindOnce(
&WebContentsViewAuraTest::OnDropComplete, base::Unretained(this)));
base::RunLoop run_loop;
async_drop_closure_ = run_loop.QuitClosure();
gfx::PointF point = {10, 10};
ui::DropTargetEvent event(*data.get(), point, point,
ui::DragDropTypes::DRAG_COPY);
view->OnDragEntered(event);
EXPECT_TRUE(drag_dest_delegate_.GetDragInitializeCalled());
view->OnPerformDrop(event, std::move(data));
run_loop.Run();
EXPECT_FALSE(drag_dest_delegate_.GetOnDropCalled());
}
IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, ContentWindowClose) { IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, ContentWindowClose) {
ASSERT_NO_FATAL_FAILURE(StartTestWithPage("/overscroll_navigation.html")); ASSERT_NO_FATAL_FAILURE(StartTestWithPage("/overscroll_navigation.html"));
......
...@@ -5,6 +5,10 @@ ...@@ -5,6 +5,10 @@
#include "content/public/browser/web_contents_view_delegate.h" #include "content/public/browser/web_contents_view_delegate.h"
#include <stddef.h> #include <stddef.h>
#include <utility>
#include "base/callback.h"
#include "content/public/common/drop_data.h"
namespace content { namespace content {
...@@ -47,4 +51,9 @@ void* WebContentsViewDelegate::CreateRenderWidgetHostViewDelegate( ...@@ -47,4 +51,9 @@ void* WebContentsViewDelegate::CreateRenderWidgetHostViewDelegate(
return nullptr; return nullptr;
} }
void WebContentsViewDelegate::OnPerformDrop(const DropData& drop_data,
DropCompletionCallback callback) {
return std::move(callback).Run(DropCompletionResult::kContinue);
}
} // namespace content } // namespace content
...@@ -22,11 +22,25 @@ class RenderFrameHost; ...@@ -22,11 +22,25 @@ class RenderFrameHost;
class RenderWidgetHost; class RenderWidgetHost;
class WebDragDestDelegate; class WebDragDestDelegate;
struct ContextMenuParams; struct ContextMenuParams;
struct DropData;
// This interface allows a client to extend the functionality of the // This interface allows a client to extend the functionality of the
// WebContentsView implementation. // WebContentsView implementation.
class CONTENT_EXPORT WebContentsViewDelegate { class CONTENT_EXPORT WebContentsViewDelegate {
public: public:
enum class DropCompletionResult {
// The drag and drop operation can continue normally.
kContinue,
// The drag and drop should be aborted. For example, the data in the
// drop does not comply with enterprise policies.
kAbort,
};
// Callback used with OnPerformDrop() method that is called once
// OnPerformDrop() completes.
using DropCompletionCallback = base::OnceCallback<void(DropCompletionResult)>;
virtual ~WebContentsViewDelegate(); virtual ~WebContentsViewDelegate();
// Returns the native window containing the WebContents, or nullptr if the // Returns the native window containing the WebContents, or nullptr if the
...@@ -67,6 +81,11 @@ class CONTENT_EXPORT WebContentsViewDelegate { ...@@ -67,6 +81,11 @@ class CONTENT_EXPORT WebContentsViewDelegate {
RenderWidgetHost* render_widget_host, RenderWidgetHost* render_widget_host,
bool is_popup); bool is_popup);
#endif #endif
// Performs the actions needed for a drop and then calls the completion
// callback once done.
virtual void OnPerformDrop(const DropData& drop_data,
DropCompletionCallback callback);
}; };
} // namespace content } // namespace content
......
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