File: show_share_ui_for_window_operation.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (187 lines) | stat: -rw-r--r-- 7,243 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// Copyright 2020 The Chromium Authors
// 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 <EventToken.h>
#include <shlobj.h>
#include <windows.applicationmodel.datatransfer.h>
#include <wrl/event.h>

#include <utility>

#include "base/functional/callback.h"
#include "base/task/single_thread_task_runner.h"
#include "base/win/core_winrt_util.h"
#include "base/win/scoped_hstring.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) {
  // 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) {
}

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;
  ComPtr<IDataTransferManager> data_transfer_manager;
  HRESULT hr = GetDataTransferManagerHandles(
      hwnd_, &data_transfer_manager_interop, &data_transfer_manager);
  if (FAILED(hr))
    return Cancel();

  // Create and register a data requested 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;
      });
  EventRegistrationToken data_requested_token{};
  hr = data_transfer_manager->add_DataRequested(
      raw_data_requested_callback.Get(), &data_requested_token);

  // Create a callback to clean up the data requested handler that doesn't rely
  // on |this| so it can still be run even if |this| has been destroyed
  auto remove_data_requested_listener = base::BindOnce(
      [](ComPtr<IDataTransferManager> data_transfer_manager,
         EventRegistrationToken data_requested_token) {
        if (data_transfer_manager && data_requested_token.value) {
          data_transfer_manager->remove_DataRequested(data_requested_token);
        }
      },
      data_transfer_manager, data_requested_token);

  // If the call to register the data requested handler failed, clean up
  // listener and cancel the operation
  if (FAILED(hr)) {
    std::move(remove_data_requested_listener).Run();
    return Cancel();
  }

  // Request showing the Share UI
  hr = data_transfer_manager_interop->ShowShareUIForWindow(hwnd_);

  // If the call is expected to complete later, save the clean-up callback for
  // later use and schedule a timeout to cover any cases where it fails (and
  // therefore never comes)
  if (SUCCEEDED(hr) && weak_ptr && data_requested_callback_) {
    remove_data_requested_listener_ = std::move(remove_data_requested_listener);
    if (!base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
            FROM_HERE,
            base::BindOnce(&ShowShareUIForWindowOperation::Cancel, weak_ptr),
            kMaxExecutionTime)) {
      return Cancel();
    }
  } else {
    // In all other cases (i.e. failure or synchronous completion), remove the
    // listener right away
    std::move(remove_data_requested_listener).Run();

    // In the failure case, also cancel the operation (if it was not already
    // cancelled synchronously).
    if (FAILED(hr) && weak_ptr)
      return Cancel();
  }
}

void ShowShareUIForWindowOperation::Cancel() {
  if (remove_data_requested_listener_)
    std::move(remove_data_requested_listener_).Run();

  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);

  // If the callback to remove the DataRequested listener has been stored on
  // |this| (i.e. this function is being invoked asynchronously), invoke it now
  // before invoking the |data_requested_callback_|, as that may result in
  // |this| being destroyed. Note that the callback to remove the DataRequested
  // listener will not have been set if this is being invoked synchronously as
  // part of the ShowShareUIForWindow call, as the system APIs don't handle
  // unregistering at that point.
  if (remove_data_requested_listener_)
    std::move(remove_data_requested_listener_).Run();

  std::move(data_requested_callback_).Run(event_args);
}

}  // namespace webshare