1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/webshare/win/share_operation.h"
6
7 #include "base/bind.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "base/task/post_task.h"
10 #include "base/win/core_winrt_util.h"
11 #include "base/win/post_async_results.h"
12 #include "base/win/scoped_hstring.h"
13 #include "base/win/vector.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/webshare/share_service_impl.h"
16 #include "chrome/browser/webshare/win/show_share_ui_for_window_operation.h"
17 #include "content/public/browser/browser_task_traits.h"
18 #include "content/public/browser/web_contents.h"
19 #include "net/base/net_errors.h"
20 #include "storage/browser/blob/blob_data_handle.h"
21 #include "storage/browser/blob/blob_storage_context.h"
22 #include "storage/browser/file_system/file_stream_writer.h"
23 #include "storage/browser/file_system/file_writer_delegate.h"
24 #include "storage/common/file_system/file_system_mount_option.h"
25 #include "ui/views/win/hwnd_util.h"
26 #include "url/gurl.h"
27
28 #include <shlobj.h>
29 #include <windows.applicationmodel.datatransfer.h>
30 #include <windows.foundation.collections.h>
31 #include <windows.foundation.h>
32 #include <windows.storage.h>
33 #include <windows.storage.streams.h>
34 #include <wininet.h>
35 #include <wrl/client.h>
36 #include <wrl/event.h>
37
38 using ABI::Windows::ApplicationModel::DataTransfer::IDataPackage;
39 using ABI::Windows::ApplicationModel::DataTransfer::IDataPackagePropertySet;
40 using ABI::Windows::ApplicationModel::DataTransfer::IDataRequest;
41 using ABI::Windows::ApplicationModel::DataTransfer::IDataRequestDeferral;
42 using ABI::Windows::ApplicationModel::DataTransfer::IDataRequestedEventArgs;
43 using ABI::Windows::Foundation::AsyncStatus;
44 using ABI::Windows::Foundation::IAsyncOperation;
45 using ABI::Windows::Foundation::IAsyncOperationCompletedHandler;
46 using ABI::Windows::Foundation::IClosable;
47 using ABI::Windows::Foundation::IUriRuntimeClass;
48 using ABI::Windows::Foundation::IUriRuntimeClassFactory;
49 using ABI::Windows::Storage::IStorageFile;
50 using ABI::Windows::Storage::IStorageFileStatics;
51 using ABI::Windows::Storage::IStorageItem;
52 using ABI::Windows::Storage::IStreamedFileDataRequestedHandler;
53 using ABI::Windows::Storage::StorageFile;
54 using ABI::Windows::Storage::Streams::IDataWriter;
55 using ABI::Windows::Storage::Streams::IDataWriterFactory;
56 using ABI::Windows::Storage::Streams::IOutputStream;
57 using Microsoft::WRL::Callback;
58 using Microsoft::WRL::ComPtr;
59 using Microsoft::WRL::Make;
60
61 namespace ABI {
62 namespace Windows {
63 namespace Foundation {
64 namespace Collections {
65
66 // Define template specializations for the types used. These uuids were randomly
67 // generated.
68 template <>
69 struct __declspec(uuid("CBE31E85-DEC8-4227-987F-9C63D6AA1A2E"))
70 IObservableVector<IStorageItem*> : IObservableVector_impl<IStorageItem*> {};
71
72 template <>
73 struct __declspec(uuid("30BE4864-5EE5-4111-916E-15126649F3C9"))
74 VectorChangedEventHandler<IStorageItem*>
75 : VectorChangedEventHandler_impl<IStorageItem*> {};
76
77 } // namespace Collections
78 } // namespace Foundation
79 } // namespace Windows
80 } // namespace ABI
81
82 namespace webshare {
83 namespace {
84
85 uint64_t g_max_file_bytes = kMaxSharedFileBytes;
86 decltype(
87 &base::win::RoGetActivationFactory) g_ro_get_activation_factory_function =
88 &base::win::RoGetActivationFactory;
89
90 template <typename InterfaceType, wchar_t const* runtime_class_id>
GetActivationFactory(InterfaceType ** factory)91 HRESULT GetActivationFactory(InterfaceType** factory) {
92 auto class_id_hstring = base::win::ScopedHString::Create(runtime_class_id);
93 if (!class_id_hstring.is_valid())
94 return E_FAIL;
95
96 return g_ro_get_activation_factory_function(class_id_hstring.get(),
97 IID_PPV_ARGS(factory));
98 }
99
100 // Implements FileStreamWriter for an IDataWriter.
101 class DataWriterFileStreamWriter final : public storage::FileStreamWriter {
102 public:
DataWriterFileStreamWriter(ComPtr<IDataWriter> data_writer,scoped_refptr<base::RefCountedData<uint64_t>> file_bytes_shared)103 explicit DataWriterFileStreamWriter(
104 ComPtr<IDataWriter> data_writer,
105 scoped_refptr<base::RefCountedData<uint64_t>> file_bytes_shared)
106 : data_writer_(data_writer), file_bytes_shared_(file_bytes_shared) {}
107
Cancel(net::CompletionOnceCallback callback)108 int Cancel(net::CompletionOnceCallback callback) final {
109 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
110 // If there is no async operation in progress, Cancel() should
111 // return net::ERR_UNEXPECTED per file_stream_header.h
112 if (!flush_operation_ && !write_operation_)
113 return net::ERR_UNEXPECTED;
114
115 if (flush_operation_) {
116 flush_callback_.Reset();
117 ComPtr<IAsyncInfo> async_info;
118 auto hr = flush_operation_.As(&async_info);
119 if (FAILED(hr))
120 return net::ERR_UNEXPECTED;
121
122 hr = async_info->Cancel();
123 if (FAILED(hr))
124 return net::ERR_UNEXPECTED;
125
126 flush_operation_.Reset();
127 }
128
129 if (write_operation_) {
130 write_callback_.Reset();
131 ComPtr<IAsyncInfo> async_info;
132 auto hr = write_operation_.As(&async_info);
133 if (FAILED(hr))
134 return net::ERR_UNEXPECTED;
135
136 hr = async_info->Cancel();
137 if (FAILED(hr))
138 return net::ERR_UNEXPECTED;
139
140 write_operation_.Reset();
141 }
142 return net::OK;
143 }
144
Flush(net::CompletionOnceCallback callback)145 int Flush(net::CompletionOnceCallback callback) final {
146 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
147 DCHECK(flush_callback_.is_null());
148 DCHECK_EQ(flush_operation_, nullptr);
149 DCHECK(write_callback_.is_null());
150 DCHECK_EQ(write_operation_, nullptr);
151
152 auto hr = data_writer_->FlushAsync(&flush_operation_);
153 if (FAILED(hr))
154 return net::ERR_UNEXPECTED;
155
156 flush_callback_ = std::move(callback);
157 base::win::PostAsyncResults(
158 flush_operation_,
159 base::BindOnce(&DataWriterFileStreamWriter::OnFlushCompleted,
160 weak_factory_.GetWeakPtr()));
161 return net::ERR_IO_PENDING;
162 }
163
Write(net::IOBuffer * buf,int buf_len,net::CompletionOnceCallback callback)164 int Write(net::IOBuffer* buf,
165 int buf_len,
166 net::CompletionOnceCallback callback) final {
167 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
168 DCHECK(flush_callback_.is_null());
169 DCHECK_EQ(flush_operation_, nullptr);
170 DCHECK(write_callback_.is_null());
171 DCHECK_EQ(write_operation_, nullptr);
172
173 // Before processing the Write request, increment the total number of file
174 // bytes shared as part of the overall Share operation this belongs to, and
175 // if it has exceeded the maximum allowed, abort writing to the streamed
176 // file.
177 file_bytes_shared_->data += buf_len;
178 if (file_bytes_shared_->data > g_max_file_bytes)
179 return net::ERR_UNEXPECTED;
180
181 auto hr =
182 data_writer_->WriteBytes(buf_len, reinterpret_cast<BYTE*>(buf->data()));
183 if (FAILED(hr))
184 return net::ERR_UNEXPECTED;
185
186 hr = data_writer_->StoreAsync(&write_operation_);
187 if (FAILED(hr))
188 return net::ERR_UNEXPECTED;
189
190 write_callback_ = std::move(callback);
191 base::win::PostAsyncResults(
192 write_operation_,
193 base::BindOnce(&DataWriterFileStreamWriter::OnWriteCompleted,
194 weak_factory_.GetWeakPtr()));
195 return net::ERR_IO_PENDING;
196 }
197
198 private:
OnFlushCompleted(boolean operation_result)199 void OnFlushCompleted(boolean operation_result) {
200 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
201 DCHECK(!flush_callback_.is_null());
202 DCHECK_NE(flush_operation_, nullptr);
203 DCHECK(write_callback_.is_null());
204 DCHECK_EQ(write_operation_, nullptr);
205
206 flush_operation_.Reset();
207 int result = operation_result == TRUE ? net::OK : net::ERR_UNEXPECTED;
208 std::move(flush_callback_).Run(result);
209 }
210
OnWriteCompleted(UINT32 operation_result)211 void OnWriteCompleted(UINT32 operation_result) {
212 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
213 DCHECK(flush_callback_.is_null());
214 DCHECK_EQ(flush_operation_, nullptr);
215 DCHECK(!write_callback_.is_null());
216 DCHECK_NE(write_operation_, nullptr);
217
218 write_operation_.Reset();
219 std::move(write_callback_).Run(operation_result);
220 }
221
222 ComPtr<IDataWriter> data_writer_;
223 scoped_refptr<base::RefCountedData<uint64_t>> file_bytes_shared_;
224 net::CompletionOnceCallback flush_callback_;
225 ComPtr<IAsyncOperation<bool>> flush_operation_;
226 net::CompletionOnceCallback write_callback_;
227 ComPtr<IAsyncOperation<UINT32>> write_operation_;
228 base::WeakPtrFactory<DataWriterFileStreamWriter> weak_factory_{this};
229 };
230
231 // Represents an ongoing operation of writing to an IOutputStream.
232 class OutputStreamWriteOperation
233 : public base::RefCounted<OutputStreamWriteOperation> {
234 public:
OutputStreamWriteOperation(content::BrowserContext::BlobContextGetter blob_context_getter,scoped_refptr<base::RefCountedData<uint64_t>> file_bytes_shared,std::string uuid)235 OutputStreamWriteOperation(
236 content::BrowserContext::BlobContextGetter blob_context_getter,
237 scoped_refptr<base::RefCountedData<uint64_t>> file_bytes_shared,
238 std::string uuid)
239 : blob_context_getter_(blob_context_getter),
240 file_bytes_shared_(file_bytes_shared),
241 uuid_(uuid) {}
242
243 // Begins the write operation on the |stream|, maintaining a reference to the
244 // |stream| until the operation is completed, at which point it will be closed
245 // (if possible) and the |on_complete| callback will be invoked. The caller
246 // is still responsible for the lifetime of this object, but not of the
247 // |stream|.
WriteStream(IOutputStream * stream,base::OnceCallback<void ()> on_complete)248 void WriteStream(IOutputStream* stream,
249 base::OnceCallback<void()> on_complete) {
250 stream_ = ComPtr<IOutputStream>(stream);
251 on_complete_ = std::move(on_complete);
252 if (!base::PostTask(
253 FROM_HERE, {content::BrowserThread::IO},
254 base::BindOnce(&OutputStreamWriteOperation::WriteStreamOnIOThread,
255 weak_factory_.GetWeakPtr())))
256 Complete();
257 }
258
259 private:
260 friend class base::RefCounted<OutputStreamWriteOperation>;
261
262 ~OutputStreamWriteOperation() = default;
263
WriteStreamOnIOThread()264 void WriteStreamOnIOThread() {
265 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
266
267 storage::BlobStorageContext* blob_storage_context =
268 blob_context_getter_.Run().get();
269 if (!blob_storage_context) {
270 Complete();
271 return;
272 }
273
274 blob_handle_ = blob_storage_context->GetBlobDataFromUUID(uuid_);
275
276 ComPtr<IDataWriterFactory> data_writer_factory;
277 auto hr =
278 GetActivationFactory<IDataWriterFactory,
279 RuntimeClass_Windows_Storage_Streams_DataWriter>(
280 &data_writer_factory);
281 if (FAILED(hr)) {
282 Complete();
283 return;
284 }
285
286 ComPtr<IDataWriter> data_writer;
287 hr = data_writer_factory->CreateDataWriter(stream_.Get(), &data_writer);
288 if (FAILED(hr)) {
289 Complete();
290 return;
291 }
292
293 writer_delegate_ = std::make_unique<storage::FileWriterDelegate>(
294 std::make_unique<DataWriterFileStreamWriter>(std::move(data_writer),
295 file_bytes_shared_),
296 storage::FlushPolicy::FLUSH_ON_COMPLETION);
297 writer_delegate_->Start(
298 blob_handle_->CreateReader(),
299 base::BindRepeating(&OutputStreamWriteOperation::OnFileWritten,
300 weak_factory_.GetWeakPtr()));
301 }
302
OnFileWritten(base::File::Error error,int64_t bytes_wrriten,storage::FileWriterDelegate::WriteProgressStatus write_status)303 void OnFileWritten(
304 base::File::Error error,
305 int64_t bytes_wrriten,
306 storage::FileWriterDelegate::WriteProgressStatus write_status) {
307 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
308 // Any status other than SUCCESS_IO_PENDING indicates completion.
309 if (write_status !=
310 storage::FileWriterDelegate::WriteProgressStatus::SUCCESS_IO_PENDING) {
311 Complete();
312 }
313 }
314
Complete()315 void Complete() {
316 // If the IOutputStream implements IClosable (e.g. the OutputStream class),
317 // close the stream whenever we are done with this operation, regardless of
318 // the outcome.
319 if (stream_) {
320 ComPtr<IClosable> closable;
321 if (SUCCEEDED(stream_.As(&closable)))
322 closable->Close();
323 }
324
325 std::move(on_complete_).Run();
326 }
327
328 content::BrowserContext::BlobContextGetter blob_context_getter_;
329 std::unique_ptr<storage::BlobDataHandle> blob_handle_;
330 scoped_refptr<base::RefCountedData<uint64_t>> file_bytes_shared_;
331 base::OnceCallback<void()> on_complete_;
332 ComPtr<IOutputStream> stream_;
333 const std::string uuid_;
334 std::unique_ptr<storage::FileWriterDelegate> writer_delegate_;
335 base::WeakPtrFactory<OutputStreamWriteOperation> weak_factory_{this};
336 };
337 } // namespace
338
339 // static
SetMaxFileBytesForTesting(uint64_t max_file_bytes)340 void ShareOperation::SetMaxFileBytesForTesting(uint64_t max_file_bytes) {
341 g_max_file_bytes = max_file_bytes;
342 }
343
344 // static
SetRoGetActivationFactoryFunctionForTesting(decltype(& base::win::RoGetActivationFactory)value)345 void ShareOperation::SetRoGetActivationFactoryFunctionForTesting(
346 decltype(&base::win::RoGetActivationFactory) value) {
347 g_ro_get_activation_factory_function = value;
348 }
349
ShareOperation(const std::string & title,const std::string & text,const GURL & url,std::vector<blink::mojom::SharedFilePtr> files,content::WebContents * web_contents)350 ShareOperation::ShareOperation(const std::string& title,
351 const std::string& text,
352 const GURL& url,
353 std::vector<blink::mojom::SharedFilePtr> files,
354 content::WebContents* web_contents)
355 : content::WebContentsObserver(web_contents),
356 title_(std::move(title)),
357 text_(std::move(text)),
358 url_(std::move(url)),
359 files_(std::move(files)) {}
360
~ShareOperation()361 ShareOperation::~ShareOperation() {
362 if (callback_)
363 Complete(blink::mojom::ShareError::CANCELED);
364 }
365
AsWeakPtr()366 base::WeakPtr<ShareOperation> ShareOperation::AsWeakPtr() {
367 return weak_factory_.GetWeakPtr();
368 }
369
Run(blink::mojom::ShareService::ShareCallback callback)370 void ShareOperation::Run(blink::mojom::ShareService::ShareCallback callback) {
371 DCHECK(!callback_);
372 callback_ = std::move(callback);
373
374 // If the required WinRT functionality is not available, or the corresponding
375 // web_contents have already been cleaned up, cancel the operation
376 const bool winrt_environment_ok =
377 base::win::ResolveCoreWinRTDelayload() &&
378 base::win::ScopedHString::ResolveCoreWinRTStringDelayload();
379 if (!winrt_environment_ok || !web_contents()) {
380 Complete(blink::mojom::ShareError::CANCELED);
381 return;
382 }
383
384 if (files_.size() > 0) {
385 // Determine the source for use with the OS IAttachmentExecute.
386 // If the source cannot be determined, does not appear to be valid,
387 // or is longer than the max length supported by the IAttachmentExecute
388 // service, use a generic value that reliably maps to the Internet zone.
389 GURL source_url = web_contents()->GetLastCommittedURL();
390 base::string16 source =
391 (source_url.is_valid() &&
392 source_url.spec().size() <= INTERNET_MAX_URL_LENGTH)
393 ? base::UTF8ToUTF16(source_url.spec())
394 : L"about:internet";
395
396 // For each "file", check against the OS that it is allowed
397 // The same instance cannot be used to check multiple files, so this
398 // makes a new one per-file. For more details on this functionality, see
399 // https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-iattachmentexecute-checkpolicy
400 for (auto& file : files_) {
401 ComPtr<IAttachmentExecute> attachment_services;
402 if (FAILED(CoCreateInstance(CLSID_AttachmentServices, nullptr, CLSCTX_ALL,
403 IID_PPV_ARGS(&attachment_services)))) {
404 Complete(blink::mojom::ShareError::INTERNAL_ERROR);
405 return;
406 }
407 if (FAILED(attachment_services->SetSource(source.c_str()))) {
408 Complete(blink::mojom::ShareError::INTERNAL_ERROR);
409 return;
410 }
411 if (FAILED(attachment_services->SetFileName(
412 base::UTF8ToWide(file->name).c_str()))) {
413 Complete(blink::mojom::ShareError::INTERNAL_ERROR);
414 return;
415 }
416 if (FAILED(attachment_services->CheckPolicy())) {
417 Complete(blink::mojom::ShareError::PERMISSION_DENIED);
418 return;
419 }
420 }
421 }
422
423 HWND hwnd =
424 views::HWNDForNativeWindow(web_contents()->GetTopLevelNativeWindow());
425 show_share_ui_for_window_operation_ =
426 std::make_unique<ShowShareUIForWindowOperation>(hwnd);
427 show_share_ui_for_window_operation_->Run(base::BindOnce(
428 &ShareOperation::OnDataRequested, weak_factory_.GetWeakPtr()));
429 }
430
OnDataRequested(IDataRequestedEventArgs * event_args)431 void ShareOperation::OnDataRequested(IDataRequestedEventArgs* event_args) {
432 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
433
434 blink::mojom::ShareError share_result;
435 if (!event_args || !web_contents()) {
436 share_result = blink::mojom::ShareError::CANCELED;
437 } else {
438 if (PutShareContentInEventArgs(event_args)) {
439 share_result = blink::mojom::ShareError::OK;
440 } else {
441 share_result = blink::mojom::ShareError::INTERNAL_ERROR;
442 }
443 }
444
445 // If the share operation failed or is not being deferred, mark it as complete
446 if (share_result != blink::mojom::ShareError::OK || !data_request_deferral_)
447 Complete(share_result);
448 }
449
PutShareContentInEventArgs(IDataRequestedEventArgs * event_args)450 bool ShareOperation::PutShareContentInEventArgs(
451 IDataRequestedEventArgs* event_args) {
452 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
453
454 ComPtr<IDataRequest> data_request;
455 if (FAILED(event_args->get_Request(&data_request)))
456 return false;
457
458 if (FAILED(data_request->get_Data(&data_package_)))
459 return false;
460
461 ComPtr<IDataPackagePropertySet> data_prop_sets;
462 if (FAILED(data_package_->get_Properties(&data_prop_sets)))
463 return false;
464
465 // Title is a required property for the UWP Share contract, so
466 // if the provided title is empty we instead use a blank value.
467 // https://docs.microsoft.com/en-us/windows/uwp/app-to-app/share-data
468 base::win::ScopedHString title_h =
469 base::win::ScopedHString::Create(title_.empty() ? " " : title_.c_str());
470 if (FAILED(data_prop_sets->put_Title(title_h.get())))
471 return false;
472
473 return PutShareContentInDataPackage(data_request.Get());
474 }
475
PutShareContentInDataPackage(IDataRequest * data_request)476 bool ShareOperation::PutShareContentInDataPackage(IDataRequest* data_request) {
477 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
478
479 if (!text_.empty()) {
480 auto text_h = base::win::ScopedHString::Create(text_);
481 if (FAILED(data_package_->SetText(text_h.get())))
482 return false;
483 }
484
485 if (!url_.spec().empty()) {
486 ComPtr<IUriRuntimeClassFactory> uri_factory;
487 auto hr =
488 GetActivationFactory<IUriRuntimeClassFactory,
489 RuntimeClass_Windows_Foundation_Uri>(&uri_factory);
490 if (FAILED(hr))
491 return hr;
492
493 auto url_h = base::win::ScopedHString::Create(url_.spec().c_str());
494 ComPtr<IUriRuntimeClass> uri;
495 if (FAILED(uri_factory->CreateUri(url_h.get(), &uri)))
496 return false;
497
498 if (FAILED(data_package_->SetUri(uri.Get())))
499 return false;
500 }
501
502 if (!files_.empty()) {
503 // Fetch a deferral to allow for async operations
504 if (FAILED(data_request->GetDeferral(&data_request_deferral_)))
505 return false;
506
507 // Initialize the output collection for the async operation(s)
508 storage_items_ = Make<base::win::Vector<IStorageItem*>>();
509
510 // Create a variable to be shared between all the operations processing the
511 // blobs to streams. This will be used to keep a running count of total file
512 // bytes shared as part of this Share operation so that if the maximum
513 // allowed is exceeded the processing can be halted. Currently the
514 // ShareOperation class is not guaranteed to outlive these operations, but
515 // if that changes in the future it may be appropriate to make this a member
516 // of the ShareOperation that is shared only be reference.
517 auto file_bytes_shared =
518 base::MakeRefCounted<base::RefCountedData<uint64_t>>(0);
519
520 ComPtr<IStorageFileStatics> storage_statics;
521 auto hr = GetActivationFactory<IStorageFileStatics,
522 RuntimeClass_Windows_Storage_StorageFile>(
523 &storage_statics);
524 if (FAILED(hr))
525 return false;
526
527 for (auto& file : files_) {
528 // This operation for converting the corresponding blob to a stream is
529 // maintained as a scoped_refptr because it may out live this
530 // ShareOperation instance. It is only invoked when the user has chosen a
531 // Share target and that target decides to start reading the contents of
532 // the corresponding IStorageFile. See
533 // https://docs.microsoft.com/en-us/uwp/api/windows.storage.storagefile.createstreamedfileasync
534 // If in the future the ShareOperation class is changed to live until the
535 // target app has finished fully processing the shared content this could
536 // be updated to be owned/maintained by this ShareOperation instance.
537 auto operation = base::MakeRefCounted<OutputStreamWriteOperation>(
538 content::BrowserContext::GetBlobStorageContext(
539 web_contents()->GetBrowserContext()),
540 file_bytes_shared, file->blob->uuid);
541 auto name_h = base::win::ScopedHString::Create(file->name);
542 auto raw_data_requested_callback =
543 Callback<IStreamedFileDataRequestedHandler>(
544 [operation](IOutputStream* stream) -> HRESULT {
545 // No additional work is needed when the write has been
546 // completed, but a callback is created to hold a reference
547 // to the |operation| until the operation has completed.
548 operation->WriteStream(
549 stream,
550 base::BindOnce(
551 base::DoNothing::Once<
552 scoped_refptr<OutputStreamWriteOperation>>(),
553 operation));
554 return S_OK;
555 });
556 // The Callback function may return null in the E_OUTOFMEMORY case
557 if (!raw_data_requested_callback)
558 return false;
559 ComPtr<IAsyncOperation<StorageFile*>> async_operation;
560 if (FAILED(storage_statics->CreateStreamedFileAsync(
561 name_h.get(), raw_data_requested_callback.Get(),
562 /*thumbnail*/ nullptr, &async_operation))) {
563 return false;
564 }
565
566 if (FAILED(base::win::PostAsyncResults(
567 async_operation,
568 base::BindOnce(&ShareOperation::OnStreamedFileCreated,
569 weak_factory_.GetWeakPtr()))))
570 return false;
571 }
572 }
573
574 return true;
575 }
576
OnStreamedFileCreated(ComPtr<IStorageFile> storage_file)577 void ShareOperation::OnStreamedFileCreated(ComPtr<IStorageFile> storage_file) {
578 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
579
580 // If there is no callback this ShareOperation already completed due to an
581 // error, so work can be halted early.
582 if (!callback_)
583 return;
584
585 if (!storage_file) {
586 Complete(blink::mojom::ShareError::INTERNAL_ERROR);
587 return;
588 }
589
590 ComPtr<IStorageItem> storage_item;
591 if (FAILED(storage_file.As(&storage_item))) {
592 Complete(blink::mojom::ShareError::INTERNAL_ERROR);
593 return;
594 }
595
596 if (FAILED(storage_items_->Append(storage_item.Get()))) {
597 Complete(blink::mojom::ShareError::INTERNAL_ERROR);
598 return;
599 }
600
601 unsigned int size;
602 if (FAILED(storage_items_->get_Size(&size))) {
603 Complete(blink::mojom::ShareError::INTERNAL_ERROR);
604 return;
605 }
606
607 // If this is not the final file, no more work to do
608 if (size != files_.size())
609 return;
610
611 if (FAILED(data_package_->SetStorageItems(storage_items_.Get(),
612 true /*readonly*/))) {
613 Complete(blink::mojom::ShareError::INTERNAL_ERROR);
614 return;
615 }
616
617 data_request_deferral_->Complete();
618 Complete(blink::mojom::ShareError::OK);
619 return;
620 }
621
Complete(const blink::mojom::ShareError share_result)622 void ShareOperation::Complete(const blink::mojom::ShareError share_result) {
623 std::move(callback_).Run(share_result);
624 }
625
626 } // namespace webshare
627