Skip to content

Commit

Permalink
A mechanism to set the default handler for a URL protocol on Windows 8.
Browse files Browse the repository at this point in the history
This change introduces OpenWithDialogController, which provides an asynchronous as well as a synchronous interface to setting a default protocol handler on Windows 8.

BUG=154081

Review URL: https://chromiumcodereview.appspot.com/11863016

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@177417 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
grt@chromium.org committed Jan 17, 2013
1 parent d28c6b2 commit e1eeaf4
Show file tree
Hide file tree
Showing 10 changed files with 1,320 additions and 0 deletions.
4 changes: 4 additions & 0 deletions tools/set_default_handler/DEPS
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
include_rules = [
"+ui",
"+win8/test",
]
25 changes: 25 additions & 0 deletions tools/set_default_handler/set_default_handler.gyp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
{
'variables': {
'chromium_code': 1,
},
'targets': [
{
'target_name': 'set_default_handler',
'type': 'executable',
'dependencies': [
'../../base/base.gyp:base',
'../../win8/win8.gyp:test_support_win8',
'../../ui/ui.gyp:ui',
],
'include_dirs': [
'../..',
],
'sources': [
'set_default_handler_main.cc',
],
},
],
}
66 changes: 66 additions & 0 deletions tools/set_default_handler/set_default_handler_main.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Makes a given program ("Google Chrome" by default) the default handler for
// some URL protocol ("http" by default) on Windows 8. These defaults can be
// overridden via the --program and --protocol command line switches.

#include <windows.h>

#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/string16.h"
#include "base/string_util.h"
#include "ui/base/win/atl_module.h"
#include "win8/test/open_with_dialog_controller.h"

namespace {

const char kSwitchProgram[] = "program";
const char kSwitchProtocol[] = "protocol";
const wchar_t kDefaultProgram[] = L"Google Chrome";
const wchar_t kDefaultProtocol[] = L"http";

} // namespace

extern "C"
int wmain(int argc, wchar_t* argv[]) {
// Initialize the commandline singleton from the environment.
CommandLine::Init(0, NULL);
// The exit manager is in charge of calling the dtors of singletons.
base::AtExitManager exit_manager;
logging::InitLogging(NULL, logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
logging::DONT_LOCK_LOG_FILE,
logging::APPEND_TO_OLD_LOG_FILE,
logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS);
logging::SetMinLogLevel(logging::LOG_VERBOSE);

ui::win::CreateATLModuleIfNeeded();

CommandLine* command_line = CommandLine::ForCurrentProcess();
string16 protocol(command_line->GetSwitchValueNative(kSwitchProtocol));
if (protocol.empty())
protocol = kDefaultProtocol;

string16 program(command_line->GetSwitchValueNative(kSwitchProgram));
if (program.empty())
program = kDefaultProgram;

std::vector<string16> choices;
HRESULT result = S_OK;
win8::OpenWithDialogController controller;
result = controller.RunSynchronously(NULL, protocol, program, &choices);

if (SUCCEEDED(result)) {
printf("success\n");
} else if (!choices.empty()) {
printf("failed to set program. possible choices: %ls\n",
JoinString(choices, L", ").c_str());
} else {
printf("failed with HRESULT: 0x08X\n", result);
}

return FAILED(result);
}
121 changes: 121 additions & 0 deletions win8/test/open_with_dialog_async.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Implementation for the asynchronous interface to the Windows shell
// SHOpenWithDialog function. The call is made on a dedicated UI thread in a
// single-threaded apartment.

#include "win8/test/open_with_dialog_async.h"

#include <shlobj.h>

#include "base/bind.h"
#include "base/callback.h"
#include "base/location.h"
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "base/thread_task_runner_handle.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
#include "base/win/windows_version.h"

namespace win8 {

namespace {

struct OpenWithContext {
OpenWithContext(
HWND parent_window_in,
const string16& file_name_in,
const string16& file_type_class_in,
int open_as_info_flags_in,
const scoped_refptr<base::SingleThreadTaskRunner>& client_runner_in,
const OpenWithDialogCallback& callback_in);
~OpenWithContext();

base::Thread thread;
HWND parent_window;
string16 file_name;
string16 file_type_class;
int open_as_info_flags;
scoped_refptr<base::SingleThreadTaskRunner> client_runner;
OpenWithDialogCallback callback;
};

OpenWithContext::OpenWithContext(
HWND parent_window_in,
const string16& file_name_in,
const string16& file_type_class_in,
int open_as_info_flags_in,
const scoped_refptr<base::SingleThreadTaskRunner>& client_runner_in,
const OpenWithDialogCallback& callback_in)
: thread("OpenWithDialog"),
parent_window(parent_window_in),
file_name(file_name_in),
file_type_class(file_type_class_in),
open_as_info_flags(open_as_info_flags_in),
client_runner(client_runner_in),
callback(callback_in) {
thread.init_com_with_mta(false);
thread.Start();
}

OpenWithContext::~OpenWithContext() {}

// Runs the caller-provided |callback| with the result of the call to
// SHOpenWithDialog on the caller's initial thread.
void OnOpenWithDialogDone(OpenWithContext* context, HRESULT result) {
DCHECK(context->client_runner->BelongsToCurrentThread());
OpenWithDialogCallback callback(context->callback);

// Join with the thread.
delete context;

// Run the client's callback.
callback.Run(result);
}

// Calls SHOpenWithDialog (blocking), and returns the result back to the client
// thread.
void OpenWithDialogTask(OpenWithContext* context) {
DCHECK_EQ(context->thread.thread_id(), base::PlatformThread::CurrentId());
OPENASINFO open_as_info = {
context->file_name.c_str(),
context->file_type_class.c_str(),
context->open_as_info_flags
};

HRESULT result = ::SHOpenWithDialog(context->parent_window, &open_as_info);

// Bounce back to the calling thread to release resources and deliver the
// callback.
if (!context->client_runner->PostTask(
FROM_HERE,
base::Bind(&OnOpenWithDialogDone, context, result))) {
// The calling thread has gone away. There's nothing to be done but leak.
// In practice this is only likely to happen at shutdown, so there isn't
// much of a concern that it'll happen in the real world.
DLOG(ERROR) << "leaking OpenWith thread; result = " << std::hex << result;
}
}

} // namespace

void OpenWithDialogAsync(
HWND parent_window,
const string16& file_name,
const string16& file_type_class,
int open_as_info_flags,
const OpenWithDialogCallback& callback) {
DCHECK_GE(base::win::GetVersion(), base::win::VERSION_VISTA);
OpenWithContext* context =
new OpenWithContext(parent_window, file_name, file_type_class,
open_as_info_flags,
base::ThreadTaskRunnerHandle::Get(), callback);
context->thread.message_loop()->PostTask(
FROM_HERE,
base::Bind(&OpenWithDialogTask, context));
}

} // namespace win8
35 changes: 35 additions & 0 deletions win8/test/open_with_dialog_async.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef WIN8_TEST_OPEN_WITH_DIALOG_ASYNC_H_
#define WIN8_TEST_OPEN_WITH_DIALOG_ASYNC_H_

#include <windows.h>

#include "base/callback_forward.h"
#include "base/string16.h"

namespace win8 {

// Expected HRESULTS:
// S_OK - A choice was made.
// HRESULT_FROM_WIN32(ERROR_CANCELLED) - The dialog was dismissed.
// HRESULT_FROM_WIN32(RPC_S_CALL_FAILED) - OpenWith.exe died.
typedef base::Callback<void(HRESULT)> OpenWithDialogCallback;

// Calls SHOpenWithDialog on a dedicated thread, returning the result to the
// caller via |callback| on the current thread. The Windows SHOpenWithDialog
// function blocks until the user makes a choice or dismisses the dialog (there
// is no natural timeout nor a means by which it can be cancelled). Note that
// the dedicated thread will be leaked if the calling thread's message loop goes
// away before the interaction completes.
void OpenWithDialogAsync(HWND parent_window,
const string16& file_name,
const string16& file_type_class,
int open_as_info_flags,
const OpenWithDialogCallback& callback);

} // namespace win8

#endif // WIN8_TEST_OPEN_WITH_DIALOG_ASYNC_H_
Loading

0 comments on commit e1eeaf4

Please sign in to comment.