forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WebShare: Add Windows Share API Wrapper
Adding a wrapper function around Window's Share operation to allow using it like a traditional async operation. Adding tests (and one more test-helper class) that verify the behavior of the wrapper function. Bug: 1035527 Change-Id: I71cc7aaa6826d53cc1273c99b603db89612f1544 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2402293 Commit-Queue: Hoch Hochkeppel <mhochk@microsoft.com> Reviewed-by: Robert Liao <robliao@chromium.org> Reviewed-by: Eric Willigers <ericwilligers@chromium.org> Cr-Commit-Position: refs/heads/master@{#811318}
- Loading branch information
Showing
7 changed files
with
555 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
73 changes: 73 additions & 0 deletions
73
chrome/browser/webshare/win/scoped_fake_data_transfer_manager_interop.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// Copyright 2020 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/webshare/win/scoped_fake_data_transfer_manager_interop.h" | ||
|
||
#include <windows.applicationmodel.datatransfer.h> | ||
#include <wrl/implements.h> | ||
|
||
#include "base/strings/string_piece.h" | ||
#include "base/win/com_init_util.h" | ||
#include "base/win/core_winrt_util.h" | ||
#include "base/win/win_util.h" | ||
#include "chrome/browser/webshare/win/fake_data_transfer_manager_interop.h" | ||
#include "chrome/browser/webshare/win/show_share_ui_for_window_operation.h" | ||
#include "testing/gtest/include/gtest/gtest.h" | ||
|
||
namespace webshare { | ||
namespace { | ||
|
||
static FakeDataTransferManagerInterop* g_current_fake_interop = nullptr; | ||
|
||
static HRESULT FakeRoGetActivationFactory(HSTRING class_id, | ||
const IID& iid, | ||
void** out_factory) { | ||
base::win::ScopedHString class_id_hstring(class_id); | ||
EXPECT_STREQ( | ||
class_id_hstring.Get().data(), | ||
RuntimeClass_Windows_ApplicationModel_DataTransfer_DataTransferManager); | ||
if (g_current_fake_interop == nullptr) { | ||
ADD_FAILURE(); | ||
return E_UNEXPECTED; | ||
} | ||
*out_factory = g_current_fake_interop; | ||
g_current_fake_interop->AddRef(); | ||
return S_OK; | ||
} | ||
|
||
} // namespace | ||
|
||
ScopedFakeDataTransferManagerInterop::ScopedFakeDataTransferManagerInterop() { | ||
// Initialization work is done in an independent function so that the | ||
// various test macros can be used. | ||
Initialize(); | ||
} | ||
|
||
ScopedFakeDataTransferManagerInterop::~ScopedFakeDataTransferManagerInterop() { | ||
g_current_fake_interop = nullptr; | ||
ShowShareUIForWindowOperation::SetRoGetActivationFactoryFunctionForTesting( | ||
&base::win::RoGetActivationFactory); | ||
} | ||
|
||
FakeDataTransferManagerInterop& | ||
ScopedFakeDataTransferManagerInterop::instance() { | ||
return *(instance_.Get()); | ||
} | ||
|
||
void ScopedFakeDataTransferManagerInterop::Initialize() { | ||
ASSERT_TRUE(base::win::ResolveCoreWinRTDelayload()); | ||
ASSERT_TRUE(base::win::ScopedHString::ResolveCoreWinRTStringDelayload()); | ||
base::win::AssertComInitialized(); | ||
|
||
instance_ = Microsoft::WRL::Make<FakeDataTransferManagerInterop>(); | ||
|
||
// Confirm there is no competing instance and set this instance | ||
// as the factory for the data_transfer_manager_util | ||
ASSERT_EQ(g_current_fake_interop, nullptr); | ||
g_current_fake_interop = instance_.Get(); | ||
ShowShareUIForWindowOperation::SetRoGetActivationFactoryFunctionForTesting( | ||
&FakeRoGetActivationFactory); | ||
} | ||
|
||
} // namespace webshare |
36 changes: 36 additions & 0 deletions
36
chrome/browser/webshare/win/scoped_fake_data_transfer_manager_interop.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// Copyright 2020 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_WEBSHARE_WIN_SCOPED_FAKE_DATA_TRANSFER_MANAGER_INTEROP_H_ | ||
#define CHROME_BROWSER_WEBSHARE_WIN_SCOPED_FAKE_DATA_TRANSFER_MANAGER_INTEROP_H_ | ||
|
||
#include <wrl/client.h> | ||
|
||
namespace webshare { | ||
|
||
class FakeDataTransferManagerInterop; | ||
|
||
// Creates and registers a FakeDataTransferManagerInterop on creation and cleans | ||
// it up on tear down, allowing GTests to easily simulate the Windows APIs used | ||
// for the Share contract. | ||
class ScopedFakeDataTransferManagerInterop { | ||
public: | ||
ScopedFakeDataTransferManagerInterop(); | ||
ScopedFakeDataTransferManagerInterop( | ||
const ScopedFakeDataTransferManagerInterop&) = delete; | ||
ScopedFakeDataTransferManagerInterop& operator=( | ||
const ScopedFakeDataTransferManagerInterop&) = delete; | ||
~ScopedFakeDataTransferManagerInterop(); | ||
|
||
FakeDataTransferManagerInterop& instance(); | ||
|
||
private: | ||
void Initialize(); | ||
|
||
Microsoft::WRL::ComPtr<FakeDataTransferManagerInterop> instance_; | ||
}; | ||
|
||
} // namespace webshare | ||
|
||
#endif // CHROME_BROWSER_WEBSHARE_WIN_SCOPED_FAKE_DATA_TRANSFER_MANAGER_INTEROP_H_ |
171 changes: 171 additions & 0 deletions
171
chrome/browser/webshare/win/show_share_ui_for_window_operation.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
// Copyright 2020 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/webshare/win/show_share_ui_for_window_operation.h" | ||
|
||
#include <shlobj.h> | ||
#include <windows.applicationmodel.datatransfer.h> | ||
#include <wrl/event.h> | ||
|
||
#include "base/callback.h" | ||
#include "base/task/post_task.h" | ||
#include "base/threading/thread_task_runner_handle.h" | ||
#include "base/win/core_winrt_util.h" | ||
#include "content/public/browser/browser_task_traits.h" | ||
#include "content/public/browser/browser_thread.h" | ||
|
||
using ABI::Windows::ApplicationModel::DataTransfer::DataRequestedEventArgs; | ||
using ABI::Windows::ApplicationModel::DataTransfer::DataTransferManager; | ||
using ABI::Windows::ApplicationModel::DataTransfer::IDataRequestedEventArgs; | ||
using ABI::Windows::ApplicationModel::DataTransfer::IDataTransferManager; | ||
using ABI::Windows::Foundation::ITypedEventHandler; | ||
using Microsoft::WRL::Callback; | ||
using Microsoft::WRL::ComPtr; | ||
|
||
namespace webshare { | ||
namespace { | ||
|
||
decltype( | ||
&base::win::RoGetActivationFactory) ro_get_activation_factory_function_ = | ||
&base::win::RoGetActivationFactory; | ||
|
||
// Fetches handles to the IDataTransferManager[Interop] instances for the | ||
// given |hwnd| | ||
HRESULT GetDataTransferManagerHandles( | ||
HWND hwnd, | ||
IDataTransferManagerInterop** data_transfer_manager_interop, | ||
IDataTransferManager** data_transfer_manager) { | ||
// If the required WinRT functionality is not available, fail the operation | ||
if (!base::win::ResolveCoreWinRTDelayload() || | ||
!base::win::ScopedHString::ResolveCoreWinRTStringDelayload()) { | ||
return E_FAIL; | ||
} | ||
|
||
// IDataTransferManagerInterop is semi-hidden behind a CloakedIid | ||
// structure on the DataTransferManager, excluding it from things | ||
// used by RoGetActivationFactory like GetIids(). Because of this, | ||
// the safe way to fetch a pointer to it is through a publicly | ||
// supported IID (e.g. IUnknown), followed by a QueryInterface call | ||
// (or something that simply wraps it like As()) to convert it. | ||
auto class_id_hstring = base::win::ScopedHString::Create( | ||
RuntimeClass_Windows_ApplicationModel_DataTransfer_DataTransferManager); | ||
if (!class_id_hstring.is_valid()) | ||
return E_FAIL; | ||
|
||
ComPtr<IUnknown> data_transfer_manager_factory; | ||
HRESULT hr = ro_get_activation_factory_function_( | ||
class_id_hstring.get(), IID_PPV_ARGS(&data_transfer_manager_factory)); | ||
if (FAILED(hr)) | ||
return hr; | ||
|
||
hr = data_transfer_manager_factory->QueryInterface( | ||
data_transfer_manager_interop); | ||
if (FAILED(hr)) | ||
return hr; | ||
|
||
hr = (*data_transfer_manager_interop) | ||
->GetForWindow(hwnd, IID_PPV_ARGS(data_transfer_manager)); | ||
return hr; | ||
} | ||
} // namespace | ||
|
||
ShowShareUIForWindowOperation::ShowShareUIForWindowOperation(HWND hwnd) | ||
: hwnd_(hwnd) { | ||
data_requested_token_.value = 0; | ||
} | ||
|
||
ShowShareUIForWindowOperation::~ShowShareUIForWindowOperation() { | ||
Cancel(); | ||
} | ||
|
||
// static | ||
void ShowShareUIForWindowOperation::SetRoGetActivationFactoryFunctionForTesting( | ||
decltype(&base::win::RoGetActivationFactory) value) { | ||
ro_get_activation_factory_function_ = value; | ||
} | ||
|
||
void ShowShareUIForWindowOperation::Run( | ||
DataRequestedCallback data_requested_callback) { | ||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | ||
|
||
data_requested_callback_ = std::move(data_requested_callback); | ||
|
||
// Fetch the OS handles needed | ||
ComPtr<IDataTransferManagerInterop> data_transfer_manager_interop; | ||
HRESULT hr = GetDataTransferManagerHandles( | ||
hwnd_, &data_transfer_manager_interop, &data_transfer_manager_); | ||
if (FAILED(hr)) | ||
return Cancel(); | ||
|
||
// Create and register a data request handler | ||
auto weak_ptr = weak_factory_.GetWeakPtr(); | ||
auto raw_data_requested_callback = Callback< | ||
ITypedEventHandler<DataTransferManager*, DataRequestedEventArgs*>>( | ||
[weak_ptr](IDataTransferManager* data_transfer_manager, | ||
IDataRequestedEventArgs* event_args) -> HRESULT { | ||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | ||
if (weak_ptr) | ||
weak_ptr.get()->OnDataRequested(data_transfer_manager, event_args); | ||
|
||
// Always return S_OK, as returning a FAILED value results in the OS | ||
// killing this process. If the data population failed the OS Share | ||
// operation will fail gracefully with messaging to the user. | ||
return S_OK; | ||
}); | ||
hr = data_transfer_manager_->add_DataRequested( | ||
raw_data_requested_callback.Get(), &data_requested_token_); | ||
if (FAILED(hr)) | ||
return Cancel(); | ||
|
||
// Request showing the Share UI | ||
show_share_ui_for_window_call_in_progress_ = true; | ||
hr = data_transfer_manager_interop->ShowShareUIForWindow(hwnd_); | ||
show_share_ui_for_window_call_in_progress_ = false; | ||
|
||
// If the call is expected to complete later, schedule a timeout to cover | ||
// any cases where it fails (and therefore never comes) | ||
if (SUCCEEDED(hr) && data_requested_callback_) { | ||
if (!base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | ||
FROM_HERE, | ||
base::BindOnce(&ShowShareUIForWindowOperation::Cancel, | ||
weak_factory_.GetWeakPtr()), | ||
kMaxExecutionTime)) { | ||
return Cancel(); | ||
} | ||
} else { | ||
RemoveDataRequestedListener(); | ||
} | ||
} | ||
|
||
void ShowShareUIForWindowOperation::Cancel() { | ||
RemoveDataRequestedListener(); | ||
if (data_requested_callback_) { | ||
std::move(data_requested_callback_).Run(nullptr); | ||
} | ||
} | ||
|
||
void ShowShareUIForWindowOperation::OnDataRequested( | ||
IDataTransferManager* data_transfer_manager, | ||
IDataRequestedEventArgs* event_args) { | ||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | ||
DCHECK_EQ(data_transfer_manager, data_transfer_manager_.Get()); | ||
|
||
// Remove the DataRequested handler if this is being invoked asynchronously. | ||
// If this is an in-progress invocation the system APIs don't handle the | ||
// event being unregistered while it is being executed, but we will unregister | ||
// it after the ShowShareUIForWindow call completes. | ||
if (!show_share_ui_for_window_call_in_progress_) | ||
RemoveDataRequestedListener(); | ||
|
||
std::move(data_requested_callback_).Run(event_args); | ||
} | ||
|
||
void ShowShareUIForWindowOperation::RemoveDataRequestedListener() { | ||
if (data_transfer_manager_ && data_requested_token_.value) { | ||
data_transfer_manager_->remove_DataRequested(data_requested_token_); | ||
data_requested_token_.value = 0; | ||
} | ||
} | ||
|
||
} // namespace webshare |
88 changes: 88 additions & 0 deletions
88
chrome/browser/webshare/win/show_share_ui_for_window_operation.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// Copyright 2020 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_WEBSHARE_WIN_SHOW_SHARE_UI_FOR_WINDOW_OPERATION_H_ | ||
#define CHROME_BROWSER_WEBSHARE_WIN_SHOW_SHARE_UI_FOR_WINDOW_OPERATION_H_ | ||
|
||
#include <EventToken.h> | ||
#include <wrl/client.h> | ||
|
||
#include "base/callback.h" | ||
#include "base/memory/weak_ptr.h" | ||
#include "base/win/core_winrt_util.h" | ||
|
||
namespace ABI { | ||
namespace Windows { | ||
namespace ApplicationModel { | ||
namespace DataTransfer { | ||
class IDataRequestedEventArgs; | ||
class IDataTransferManager; | ||
} // namespace DataTransfer | ||
} // namespace ApplicationModel | ||
} // namespace Windows | ||
} // namespace ABI | ||
|
||
namespace webshare { | ||
|
||
// Represents a call to ShowShareUIForWindow in an async fashion. | ||
class ShowShareUIForWindowOperation { | ||
public: | ||
using DataRequestedCallback = base::OnceCallback<void( | ||
ABI::Windows::ApplicationModel::DataTransfer::IDataRequestedEventArgs*)>; | ||
|
||
explicit ShowShareUIForWindowOperation(const HWND hwnd); | ||
ShowShareUIForWindowOperation(const ShowShareUIForWindowOperation&) = delete; | ||
ShowShareUIForWindowOperation& operator=( | ||
const ShowShareUIForWindowOperation&) = delete; | ||
~ShowShareUIForWindowOperation(); | ||
|
||
// Test hook for overriding the base RoGetActivationFactory function | ||
static void SetRoGetActivationFactoryFunctionForTesting( | ||
decltype(&base::win::RoGetActivationFactory) value); | ||
|
||
static constexpr base::TimeDelta max_execution_time_for_testing() { | ||
return kMaxExecutionTime; | ||
} | ||
|
||
// Requests the Window's Share operation for the previously supplied |hwnd| | ||
// and uses the |data_requested_callback| to supply the operation with the | ||
// data to share. This call does not impact the lifetime of this class, so the | ||
// caller must keep this instance alive until it has completed or the caller | ||
// no longer desires the operation to continue. | ||
// | ||
// The provided |data_requested_callback| will be invoked either | ||
// synchronously as part of this call, or asynchronously at a | ||
// later point when the OS Share operation requests it. In both cases, when | ||
// the |data_requested_callback| is invoked it will be on the UI thread with | ||
// |IDataRequestedEventArgs| from the OS. If an error is encountered the | ||
// |data_requested_callback| will be invoked without any arguments. | ||
// | ||
// This should only be called from the UI thread. | ||
void Run(DataRequestedCallback data_requested_callback); | ||
|
||
private: | ||
static constexpr base::TimeDelta kMaxExecutionTime = | ||
base::TimeDelta::FromSeconds(30); | ||
|
||
void Cancel(); | ||
void OnDataRequested( | ||
ABI::Windows::ApplicationModel::DataTransfer::IDataTransferManager* | ||
data_transfer_manager, | ||
ABI::Windows::ApplicationModel::DataTransfer::IDataRequestedEventArgs* | ||
event_args); | ||
void RemoveDataRequestedListener(); | ||
|
||
DataRequestedCallback data_requested_callback_; | ||
EventRegistrationToken data_requested_token_; | ||
Microsoft::WRL::ComPtr< | ||
ABI::Windows::ApplicationModel::DataTransfer::IDataTransferManager> | ||
data_transfer_manager_; | ||
const HWND hwnd_; | ||
bool show_share_ui_for_window_call_in_progress_; | ||
base::WeakPtrFactory<ShowShareUIForWindowOperation> weak_factory_{this}; | ||
}; | ||
|
||
} // namespace webshare | ||
|
||
#endif // CHROME_BROWSER_WEBSHARE_WIN_SHOW_SHARE_UI_FOR_WINDOW_OPERATION_H_ |
Oops, something went wrong.