Home | History | Annotate | Download | only in test
      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 
     23 namespace win8 {
     24 
     25 namespace {
     26 
     27 struct 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 
     46 OpenWithContext::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 
     64 OpenWithContext::~OpenWithContext() {}
     65 
     66 // Runs the caller-provided |callback| with the result of the call to
     67 // SHOpenWithDialog on the caller's initial thread.
     68 void 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.
     81 void 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 
    105 void 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