1// Copyright (c) 2013 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// Implementation for the asynchronous interface to the Windows shell
6// SHOpenWithDialog function.  The call is made on a dedicated UI thread in a
7// single-threaded apartment.
8
9#include "win8/test/open_with_dialog_async.h"
10
11#include <shlobj.h>
12
13#include "base/bind.h"
14#include "base/callback.h"
15#include "base/location.h"
16#include "base/memory/ref_counted.h"
17#include "base/single_thread_task_runner.h"
18#include "base/thread_task_runner_handle.h"
19#include "base/threading/platform_thread.h"
20#include "base/threading/thread.h"
21#include "base/win/windows_version.h"
22
23namespace win8 {
24
25namespace {
26
27struct OpenWithContext {
28  OpenWithContext(
29      HWND parent_window_in,
30      const base::string16& file_name_in,
31      const base::string16& file_type_class_in,
32      int open_as_info_flags_in,
33      const scoped_refptr<base::SingleThreadTaskRunner>& client_runner_in,
34      const OpenWithDialogCallback& callback_in);
35  ~OpenWithContext();
36
37  base::Thread thread;
38  HWND parent_window;
39  base::string16 file_name;
40  base::string16 file_type_class;
41  int open_as_info_flags;
42  scoped_refptr<base::SingleThreadTaskRunner> client_runner;
43  OpenWithDialogCallback callback;
44};
45
46OpenWithContext::OpenWithContext(
47    HWND parent_window_in,
48    const base::string16& file_name_in,
49    const base::string16& file_type_class_in,
50    int open_as_info_flags_in,
51    const scoped_refptr<base::SingleThreadTaskRunner>& client_runner_in,
52    const OpenWithDialogCallback& callback_in)
53    : thread("OpenWithDialog"),
54      parent_window(parent_window_in),
55      file_name(file_name_in),
56      file_type_class(file_type_class_in),
57      open_as_info_flags(open_as_info_flags_in),
58      client_runner(client_runner_in),
59      callback(callback_in) {
60  thread.init_com_with_mta(false);
61  thread.Start();
62}
63
64OpenWithContext::~OpenWithContext() {}
65
66// Runs the caller-provided |callback| with the result of the call to
67// SHOpenWithDialog on the caller's initial thread.
68void OnOpenWithDialogDone(OpenWithContext* context, HRESULT result) {
69  DCHECK(context->client_runner->BelongsToCurrentThread());
70  OpenWithDialogCallback callback(context->callback);
71
72  // Join with the thread.
73  delete context;
74
75  // Run the client's callback.
76  callback.Run(result);
77}
78
79// Calls SHOpenWithDialog (blocking), and returns the result back to the client
80// thread.
81void OpenWithDialogTask(OpenWithContext* context) {
82  DCHECK_EQ(context->thread.thread_id(), base::PlatformThread::CurrentId());
83  OPENASINFO open_as_info = {
84    context->file_name.c_str(),
85    context->file_type_class.c_str(),
86    context->open_as_info_flags
87  };
88
89  HRESULT result = ::SHOpenWithDialog(context->parent_window, &open_as_info);
90
91  // Bounce back to the calling thread to release resources and deliver the
92  // callback.
93  if (!context->client_runner->PostTask(
94          FROM_HERE,
95          base::Bind(&OnOpenWithDialogDone, context, result))) {
96    // The calling thread has gone away. There's nothing to be done but leak.
97    // In practice this is only likely to happen at shutdown, so there isn't
98    // much of a concern that it'll happen in the real world.
99    DLOG(ERROR) << "leaking OpenWith thread; result = " << std::hex << result;
100  }
101}
102
103}  // namespace
104
105void OpenWithDialogAsync(
106    HWND parent_window,
107    const base::string16& file_name,
108    const base::string16& file_type_class,
109    int open_as_info_flags,
110    const OpenWithDialogCallback& callback) {
111  DCHECK_GE(base::win::GetVersion(), base::win::VERSION_VISTA);
112  OpenWithContext* context =
113      new OpenWithContext(parent_window, file_name, file_type_class,
114                          open_as_info_flags,
115                          base::ThreadTaskRunnerHandle::Get(), callback);
116  context->thread.message_loop()->PostTask(
117      FROM_HERE,
118      base::Bind(&OpenWithDialogTask, context));
119}
120
121}  // namespace win8
122