12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "win8/test/open_with_dialog_controller.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <shlobj.h>
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/bind.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/callback.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/logging.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/memory/scoped_ptr.h"
139ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch#include "base/message_loop/message_loop.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/run_loop.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/thread_task_runner_handle.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/threading/thread_checker.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/win/windows_version.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "win8/test/open_with_dialog_async.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "win8/test/ui_automation_client.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace win8 {
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const int kControllerTimeoutSeconds = 5;
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const wchar_t kShellFlyoutClassName[] = L"Shell_Flyout";
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// A callback invoked with the OpenWithDialogController's results. Said results
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// are copied to |result_out| and |choices_out| and then |closure| is invoked.
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// This function is in support of OpenWithDialogController::RunSynchronously.
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void OnMakeDefaultComplete(
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::Closure& closure,
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    HRESULT* result_out,
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    std::vector<base::string16>* choices_out,
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    HRESULT hr,
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    std::vector<base::string16> choices) {
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  *result_out = hr;
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  *choices_out = choices;
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  closure.Run();
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Lives on the main thread and is owned by a controller. May outlive the
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// controller (see Orphan).
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class OpenWithDialogController::Context {
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public:
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Context();
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ~Context();
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::WeakPtr<Context> AsWeakPtr();
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void Orphan();
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void Begin(HWND parent_window,
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)             const base::string16& url_protocol,
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)             const base::string16& program_name,
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)             const OpenWithDialogController::SetDefaultCallback& callback);
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private:
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  enum State {
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // The Context has been constructed.
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    CONTEXT_INITIALIZED,
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // The UI automation event handler is ready.
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    CONTEXT_AUTOMATION_READY,
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // The automation results came back before the call to SHOpenWithDialog.
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    CONTEXT_WAITING_FOR_DIALOG,
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // The call to SHOpenWithDialog returned before automation results.
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    CONTEXT_WAITING_FOR_RESULTS,
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    CONTEXT_FINISHED,
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  };
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Invokes the client's callback and destroys this instance.
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void NotifyClientAndDie();
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void OnTimeout();
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void OnInitialized(HRESULT result);
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  void OnAutomationResult(HRESULT result, std::vector<base::string16> choices);
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void OnOpenWithComplete(HRESULT result);
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::ThreadChecker thread_checker_;
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  State state_;
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  internal::UIAutomationClient automation_client_;
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  HWND parent_window_;
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 file_name_;
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 file_type_class_;
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int open_as_info_flags_;
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  OpenWithDialogController::SetDefaultCallback callback_;
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  HRESULT open_with_result_;
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  HRESULT automation_result_;
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::vector<base::string16> automation_choices_;
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::WeakPtrFactory<Context> weak_ptr_factory_;
93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(Context);
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)OpenWithDialogController::Context::Context()
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : state_(CONTEXT_INITIALIZED),
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      parent_window_(),
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      open_as_info_flags_(),
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      open_with_result_(E_FAIL),
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      automation_result_(E_FAIL),
102c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      weak_ptr_factory_(this) {}
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)OpenWithDialogController::Context::~Context() {
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)base::WeakPtr<OpenWithDialogController::Context>
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    OpenWithDialogController::Context::AsWeakPtr() {
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return weak_ptr_factory_.GetWeakPtr();
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void OpenWithDialogController::Context::Orphan() {
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The controller is being destroyed. Its client is no longer interested in
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // having the interaction continue.
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DLOG_IF(WARNING, (state_ == CONTEXT_AUTOMATION_READY ||
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    state_ == CONTEXT_WAITING_FOR_DIALOG))
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      << "Abandoning the OpenWithDialog.";
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  delete this;
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void OpenWithDialogController::Context::Begin(
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    HWND parent_window,
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const base::string16& url_protocol,
1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const base::string16& program_name,
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const OpenWithDialogController::SetDefaultCallback& callback) {
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  parent_window_ = parent_window;
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  file_name_ = url_protocol;
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  file_type_class_.clear();
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  open_as_info_flags_ = (OAIF_URL_PROTOCOL | OAIF_FORCE_REGISTRATION |
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         OAIF_REGISTER_EXT);
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  callback_ = callback;
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Post a delayed callback to abort the operation if it takes too long.
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      FROM_HERE,
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&OpenWithDialogController::Context::OnTimeout, AsWeakPtr()),
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::TimeDelta::FromSeconds(kControllerTimeoutSeconds));
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  automation_client_.Begin(
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      kShellFlyoutClassName,
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      program_name,
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&OpenWithDialogController::Context::OnInitialized,
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 AsWeakPtr()),
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&OpenWithDialogController::Context::OnAutomationResult,
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 AsWeakPtr()));
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void OpenWithDialogController::Context::NotifyClientAndDie() {
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(state_, CONTEXT_FINISHED);
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DLOG_IF(WARNING, SUCCEEDED(automation_result_) && FAILED(open_with_result_))
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      << "Automation succeeded, yet SHOpenWithDialog failed.";
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Ignore any future callbacks (such as the timeout) or calls to Orphan.
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  weak_ptr_factory_.InvalidateWeakPtrs();
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  callback_.Run(automation_result_, automation_choices_);
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  delete this;
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void OpenWithDialogController::Context::OnTimeout() {
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // This is a LOG rather than a DLOG since it represents something that needs
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // to be investigated and fixed.
170116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  LOG(ERROR) << __FUNCTION__ << " state: " << state_;
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  state_ = CONTEXT_FINISHED;
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  NotifyClientAndDie();
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void OpenWithDialogController::Context::OnInitialized(HRESULT result) {
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(state_, CONTEXT_INITIALIZED);
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (FAILED(result)) {
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    automation_result_ = result;
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    state_ = CONTEXT_FINISHED;
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    NotifyClientAndDie();
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  state_ = CONTEXT_AUTOMATION_READY;
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  OpenWithDialogAsync(
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      parent_window_, file_name_, file_type_class_, open_as_info_flags_,
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&OpenWithDialogController::Context::OnOpenWithComplete,
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 weak_ptr_factory_.GetWeakPtr()));
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void OpenWithDialogController::Context::OnAutomationResult(
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    HRESULT result,
1945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    std::vector<base::string16> choices) {
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(automation_result_, E_FAIL);
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  automation_result_ = result;
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  automation_choices_ = choices;
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  switch (state_) {
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case CONTEXT_AUTOMATION_READY:
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // The results of automation are in and we're waiting for
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // SHOpenWithDialog to return.
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      state_ = CONTEXT_WAITING_FOR_DIALOG;
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case CONTEXT_WAITING_FOR_RESULTS:
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      state_ = CONTEXT_FINISHED;
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      NotifyClientAndDie();
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    default:
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      NOTREACHED() << state_;
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void OpenWithDialogController::Context::OnOpenWithComplete(HRESULT result) {
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(open_with_result_, E_FAIL);
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  open_with_result_ = result;
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  switch (state_) {
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case CONTEXT_AUTOMATION_READY:
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // The interaction completed and we're waiting for the results from the
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // automation side to come in.
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      state_ = CONTEXT_WAITING_FOR_RESULTS;
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case CONTEXT_WAITING_FOR_DIALOG:
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // All results are in.  Invoke the caller's callback.
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      state_ = CONTEXT_FINISHED;
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      NotifyClientAndDie();
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      break;
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    default:
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      NOTREACHED() << state_;
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)OpenWithDialogController::OpenWithDialogController() {}
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)OpenWithDialogController::~OpenWithDialogController() {
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Orphan the context if this instance is being destroyed before the context
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // finishes its work.
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (context_)
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    context_->Orphan();
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void OpenWithDialogController::Begin(
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    HWND parent_window,
2475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const base::string16& url_protocol,
2485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const base::string16& program,
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const SetDefaultCallback& callback) {
250868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  DCHECK_EQ(context_.get(), static_cast<Context*>(NULL));
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (base::win::GetVersion() < base::win::VERSION_WIN8) {
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    NOTREACHED() << "Windows 8 is required.";
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // The callback may not properly handle being run from Begin, so post a task
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // to this thread's task runner to call it.
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::ThreadTaskRunnerHandle::Get()->PostTask(
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        FROM_HERE,
2575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        base::Bind(callback, E_FAIL, std::vector<base::string16>()));
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  context_ = (new Context())->AsWeakPtr();
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  context_->Begin(parent_window, url_protocol, program, callback);
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)HRESULT OpenWithDialogController::RunSynchronously(
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    HWND parent_window,
2675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const base::string16& protocol,
2685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const base::string16& program,
2695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    std::vector<base::string16>* choices) {
270c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK_EQ(base::MessageLoop::current(),
271c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            static_cast<base::MessageLoop*>(NULL));
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (base::win::GetVersion() < base::win::VERSION_WIN8) {
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    NOTREACHED() << "Windows 8 is required.";
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return E_FAIL;
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  HRESULT result = S_OK;
278c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::MessageLoop message_loop;
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::RunLoop run_loop;
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  message_loop.PostTask(
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      FROM_HERE,
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&OpenWithDialogController::Begin, base::Unretained(this),
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 parent_window, protocol, program,
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 Bind(&OnMakeDefaultComplete, run_loop.QuitClosure(),
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                      &result, choices)));
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  run_loop.Run();
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return result;
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace win8
293