summaryrefslogtreecommitdiffstats
path: root/win8
diff options
context:
space:
mode:
authorsamli <samli@chromium.org>2016-01-19 19:44:56 -0800
committerCommit bot <commit-bot@chromium.org>2016-01-20 03:46:11 +0000
commit0e3a9fd80a26f1fce889200d330d79e64d7a2586 (patch)
treeb81e8346effefb053d8ad102efd2a31c0507bd13 /win8
parent15d94b7c0e0b17e973d144c03c7f69f9e0f62309 (diff)
downloadchromium_src-0e3a9fd80a26f1fce889200d330d79e64d7a2586.zip
chromium_src-0e3a9fd80a26f1fce889200d330d79e64d7a2586.tar.gz
chromium_src-0e3a9fd80a26f1fce889200d330d79e64d7a2586.tar.bz2
Revert of Remove remote tree host and some related input and metro_driver code (patchset #5 id:80001 of https://codereview.chromium.org/1586843002/ )
Reason for revert: Suspecting that this is breaking the Webkit Win (dbg) builder. First failed build: https://build.chromium.org/p/chromium.webkit/builders/WebKit%20Win%20Builder%20%28dbg%29/builds/88362 Original issue's description: > Remove remote tree host and some related input and metro_driver code > > Part of continued stripping out of Metro/Win8/Immersive mode. > > BUG=558054 > > Committed: https://crrev.com/5cc04f70e424020d8afe49bdc4c1c76683f0ea7b > Cr-Commit-Position: refs/heads/master@{#370068} TBR=sky@chromium.org,thestig@chromium.org,shuchen@chromium.org,ananta@chromium.org,dpranke@chromium.org,scottmg@chromium.org NOTRY=true NOPRESUBMIT=true NOTREECHECKS=true BUG=558054 Review URL: https://codereview.chromium.org/1602403002 Cr-Commit-Position: refs/heads/master@{#370290}
Diffstat (limited to 'win8')
-rw-r--r--win8/BUILD.gn25
-rw-r--r--win8/delegate_execute/BUILD.gn4
-rw-r--r--win8/delegate_execute/command_execute_impl.cc15
-rw-r--r--win8/delegate_execute/delegate_execute.gyp5
-rw-r--r--win8/metro_driver/BUILD.gn10
-rw-r--r--win8/metro_driver/chrome_app_view_ash.cc1464
-rw-r--r--win8/metro_driver/chrome_app_view_ash.h262
-rw-r--r--win8/metro_driver/file_picker.cc622
-rw-r--r--win8/metro_driver/file_picker.h18
-rw-r--r--win8/metro_driver/file_picker_ash.cc619
-rw-r--r--win8/metro_driver/file_picker_ash.h157
-rw-r--r--win8/metro_driver/metro_driver.cc135
-rw-r--r--win8/metro_driver/metro_driver.gyp12
-rw-r--r--win8/metro_driver/metro_driver.h17
-rw-r--r--win8/metro_driver/metro_driver_win7.cc1229
-rw-r--r--win8/viewer/metro_viewer_constants.cc13
-rw-r--r--win8/viewer/metro_viewer_constants.h20
-rw-r--r--win8/viewer/metro_viewer_exports.h29
-rw-r--r--win8/viewer/metro_viewer_process_host.cc346
-rw-r--r--win8/viewer/metro_viewer_process_host.h221
-rw-r--r--win8/win8.gyp29
21 files changed, 5250 insertions, 2 deletions
diff --git a/win8/BUILD.gn b/win8/BUILD.gn
index 7b36f33..fb43d2d 100644
--- a/win8/BUILD.gn
+++ b/win8/BUILD.gn
@@ -2,6 +2,31 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+source_set("metro_viewer_constants") {
+ sources = [
+ "viewer/metro_viewer_constants.cc",
+ "viewer/metro_viewer_constants.h",
+ ]
+}
+
+component("metro_viewer") {
+ sources = [
+ "viewer/metro_viewer_process_host.cc",
+ "viewer/metro_viewer_process_host.h",
+ ]
+
+ defines = [ "METRO_VIEWER_IMPLEMENTATION" ]
+
+ deps = [
+ ":metro_viewer_constants",
+ "//base",
+ "//ipc",
+ "//ui/aura",
+ "//ui/gfx",
+ "//ui/metro_viewer",
+ ]
+}
+
source_set("test_support_win8") {
sources = [
"test/open_with_dialog_async.cc",
diff --git a/win8/delegate_execute/BUILD.gn b/win8/delegate_execute/BUILD.gn
index 4b7d911..8309dc6 100644
--- a/win8/delegate_execute/BUILD.gn
+++ b/win8/delegate_execute/BUILD.gn
@@ -50,6 +50,10 @@ source_set("lib") {
"//ui/gfx/geometry",
]
+ if (use_aura) {
+ public_deps += [ "//win8:metro_viewer_constants" ]
+ }
+
if (is_chrome_branded) {
public_deps += [ "//google_update" ]
}
diff --git a/win8/delegate_execute/command_execute_impl.cc b/win8/delegate_execute/command_execute_impl.cc
index bae9ee8..d72da85984 100644
--- a/win8/delegate_execute/command_execute_impl.cc
+++ b/win8/delegate_execute/command_execute_impl.cc
@@ -32,6 +32,7 @@
#include "ui/gfx/win/dpi.h"
#include "win8/delegate_execute/chrome_util.h"
#include "win8/delegate_execute/delegate_execute_util.h"
+#include "win8/viewer/metro_viewer_constants.h"
namespace {
// Helper function to retrieve the url from IShellItem interface passed in.
@@ -180,8 +181,18 @@ STDMETHODIMP CommandExecuteImpl::GetValue(enum AHE_TYPE* pahe) {
return E_FAIL;
}
- // TODO(scottmg): Can all go eventually https://crbug.com/558054.
- *pahe = AHE_DESKTOP;
+ EC_HOST_UI_MODE mode = GetLaunchMode();
+ *pahe = (mode == ECHUIM_DESKTOP) ? AHE_DESKTOP : AHE_IMMERSIVE;
+
+ // If we're going to return AHE_IMMERSIVE, then both the browser process and
+ // the metro viewer need to launch and connect before the user can start
+ // browsing. However we must not launch the metro viewer until we get a
+ // call to CommandExecuteImpl::Execute(). If we wait until then to launch
+ // the browser process as well, it will appear laggy while they connect to
+ // each other, so we pre-launch the browser process now.
+ if (*pahe == AHE_IMMERSIVE && verb_ != win8::kMetroViewerConnectVerb) {
+ LaunchChromeBrowserProcess();
+ }
return S_OK;
}
diff --git a/win8/delegate_execute/delegate_execute.gyp b/win8/delegate_execute/delegate_execute.gyp
index b863dc5..8dcd71f 100644
--- a/win8/delegate_execute/delegate_execute.gyp
+++ b/win8/delegate_execute/delegate_execute.gyp
@@ -73,6 +73,11 @@
},
},
'conditions': [
+ ['use_aura==1', {
+ 'dependencies': [
+ '../win8.gyp:metro_viewer_constants',
+ ],
+ }],
['branding!="Chrome"', {
'dependencies!': [
'../../google_update/google_update.gyp:google_update',
diff --git a/win8/metro_driver/BUILD.gn b/win8/metro_driver/BUILD.gn
index fc66690..68c9a90 100644
--- a/win8/metro_driver/BUILD.gn
+++ b/win8/metro_driver/BUILD.gn
@@ -10,6 +10,9 @@ shared_library("metro_driver") {
sources = [
"display_properties.cc",
"display_properties.h",
+ "metro_driver.cc",
+ "metro_driver.h",
+ "metro_driver_win7.cc",
"stdafx.h",
"winrt_utils.cc",
"winrt_utils.h",
@@ -33,12 +36,17 @@ shared_library("metro_driver") {
if (use_aura) {
sources += [
+ "chrome_app_view_ash.cc",
+ "chrome_app_view_ash.h",
"direct3d_helper.cc",
"direct3d_helper.h",
+ "file_picker_ash.cc",
+ "file_picker_ash.h",
]
deps += [
"//ui/events:gesture_detection",
+ "//win8:metro_viewer_constants",
"//win8/metro_driver/ime",
]
} else {
@@ -49,6 +57,8 @@ shared_library("metro_driver") {
"chrome_url_launch_handler.h",
"devices_handler.cc",
"devices_handler.h",
+ "file_picker.cc",
+ "file_picker.h",
"metro_dialog_box.cc",
"metro_dialog_box.h",
"print_document_source.cc",
diff --git a/win8/metro_driver/chrome_app_view_ash.cc b/win8/metro_driver/chrome_app_view_ash.cc
new file mode 100644
index 0000000..01f9ba2
--- /dev/null
+++ b/win8/metro_driver/chrome_app_view_ash.cc
@@ -0,0 +1,1464 @@
+// Copyright (c) 2012 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.
+
+#include "win8/metro_driver/stdafx.h"
+#include "win8/metro_driver/chrome_app_view_ash.h"
+
+#include <corewindow.h>
+#include <shellapi.h>
+#include <stdint.h>
+#include <windows.foundation.h>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/path_service.h"
+#include "base/single_thread_task_runner.h"
+#include "base/win/windows_version.h"
+#include "chrome/common/chrome_switches.h"
+#include "ipc/ipc_channel.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_sender.h"
+#include "ui/events/gesture_detection/motion_event.h"
+#include "ui/events/win/system_event_state_lookup.h"
+#include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/win/dpi.h"
+#include "ui/metro_viewer/metro_viewer_messages.h"
+#include "win8/metro_driver/file_picker_ash.h"
+#include "win8/metro_driver/ime/ime_popup_monitor.h"
+#include "win8/metro_driver/ime/input_source.h"
+#include "win8/metro_driver/ime/text_service.h"
+#include "win8/metro_driver/metro_driver.h"
+#include "win8/metro_driver/winrt_utils.h"
+#include "win8/viewer/metro_viewer_constants.h"
+
+typedef winfoundtn::ITypedEventHandler<
+ winapp::Core::CoreApplicationView*,
+ winapp::Activation::IActivatedEventArgs*> ActivatedHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::PointerEventArgs*> PointerEventHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::KeyEventArgs*> KeyEventHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreDispatcher*,
+ winui::Core::AcceleratorKeyEventArgs*> AcceleratorKeyEventHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::CharacterReceivedEventArgs*> CharEventHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::WindowActivatedEventArgs*> WindowActivatedHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::WindowSizeChangedEventArgs*> SizeChangedHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Input::EdgeGesture*,
+ winui::Input::EdgeGestureEventArgs*> EdgeEventHandler;
+
+// This function is exported by chrome.exe.
+typedef int (__cdecl *BreakpadExceptionHandler)(EXCEPTION_POINTERS* info);
+
+// Global information used across the metro driver.
+struct Globals {
+ winapp::Activation::ApplicationExecutionState previous_state;
+ winapp::Core::ICoreApplicationExit* app_exit;
+ BreakpadExceptionHandler breakpad_exception_handler;
+} globals;
+
+extern float GetModernUIScale();
+
+namespace {
+
+enum KeyModifier {
+ NONE,
+ SHIFT = 1,
+ CONTROL = 2,
+ ALT = 4
+};
+
+const int kChromeChannelPollTimerMs = 100;
+
+// Helper function to send keystrokes via the SendInput function.
+// mnemonic_char: The keystroke to be sent.
+// modifiers: Combination with Alt, Ctrl, Shift, etc.
+void SendKeySequence(
+ WORD mnemonic_char, KeyModifier modifiers) {
+ INPUT keys[4] = {}; // Keyboard events
+ int key_count = 0; // Number of generated events
+
+ if (modifiers & SHIFT) {
+ keys[key_count].type = INPUT_KEYBOARD;
+ keys[key_count].ki.wVk = VK_SHIFT;
+ keys[key_count].ki.wScan = MapVirtualKey(VK_SHIFT, 0);
+ key_count++;
+ }
+
+ if (modifiers & CONTROL) {
+ keys[key_count].type = INPUT_KEYBOARD;
+ keys[key_count].ki.wVk = VK_CONTROL;
+ keys[key_count].ki.wScan = MapVirtualKey(VK_CONTROL, 0);
+ key_count++;
+ }
+
+ if (modifiers & ALT) {
+ keys[key_count].type = INPUT_KEYBOARD;
+ keys[key_count].ki.wVk = VK_MENU;
+ keys[key_count].ki.wScan = MapVirtualKey(VK_MENU, 0);
+ key_count++;
+ }
+
+ keys[key_count].type = INPUT_KEYBOARD;
+ keys[key_count].ki.wVk = mnemonic_char;
+ keys[key_count].ki.wScan = MapVirtualKey(mnemonic_char, 0);
+ key_count++;
+
+ bool should_sleep = key_count > 1;
+
+ // Send key downs.
+ for (int i = 0; i < key_count; i++) {
+ SendInput(1, &keys[ i ], sizeof(keys[0]));
+ keys[i].ki.dwFlags |= KEYEVENTF_KEYUP;
+ if (should_sleep)
+ Sleep(10);
+ }
+
+ // Now send key ups in reverse order.
+ for (int i = key_count; i; i--) {
+ SendInput(1, &keys[ i - 1 ], sizeof(keys[0]));
+ if (should_sleep)
+ Sleep(10);
+ }
+}
+
+class ChromeChannelListener : public IPC::Listener {
+ public:
+ ChromeChannelListener(base::MessageLoop* ui_loop, ChromeAppViewAsh* app_view)
+ : ui_task_runner_(ui_loop->task_runner()), app_view_(app_view) {}
+
+ bool OnMessageReceived(const IPC::Message& message) override {
+ IPC_BEGIN_MESSAGE_MAP(ChromeChannelListener, message)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_ActivateDesktop,
+ OnActivateDesktop)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_MetroExit, OnMetroExit)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_OpenURLOnDesktop,
+ OnOpenURLOnDesktop)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SetCursor, OnSetCursor)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_DisplayFileOpen,
+ OnDisplayFileOpenDialog)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_DisplayFileSaveAs,
+ OnDisplayFileSaveAsDialog)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_DisplaySelectFolder,
+ OnDisplayFolderPicker)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SetCursorPos, OnSetCursorPos)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_ImeCancelComposition,
+ OnImeCancelComposition)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_ImeTextInputClientUpdated,
+ OnImeTextInputClientChanged)
+ IPC_MESSAGE_UNHANDLED(__debugbreak())
+ IPC_END_MESSAGE_MAP()
+ return true;
+ }
+
+ void OnChannelError() override {
+ DVLOG(1) << "Channel error. Exiting.";
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&ChromeAppViewAsh::OnMetroExit, base::Unretained(app_view_),
+ TERMINATE_USING_KEY_SEQUENCE));
+
+ // In early Windows 8 versions the code above sometimes fails so we call
+ // it a second time with a NULL window which just calls Exit().
+ ui_task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&ChromeAppViewAsh::OnMetroExit, base::Unretained(app_view_),
+ TERMINATE_USING_PROCESS_EXIT),
+ base::TimeDelta::FromMilliseconds(100));
+ }
+
+ private:
+ void OnActivateDesktop(const base::FilePath& shortcut, bool ash_exit) {
+ ui_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&ChromeAppViewAsh::OnActivateDesktop,
+ base::Unretained(app_view_), shortcut, ash_exit));
+ }
+
+ void OnMetroExit() {
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&ChromeAppViewAsh::OnMetroExit, base::Unretained(app_view_),
+ TERMINATE_USING_KEY_SEQUENCE));
+ }
+
+ void OnOpenURLOnDesktop(const base::FilePath& shortcut,
+ const base::string16& url) {
+ ui_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&ChromeAppViewAsh::OnOpenURLOnDesktop,
+ base::Unretained(app_view_), shortcut, url));
+ }
+
+ void OnSetCursor(int64_t cursor) {
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&ChromeAppViewAsh::OnSetCursor, base::Unretained(app_view_),
+ reinterpret_cast<HCURSOR>(cursor)));
+ }
+
+ void OnDisplayFileOpenDialog(const base::string16& title,
+ const base::string16& filter,
+ const base::FilePath& default_path,
+ bool allow_multiple_files) {
+ ui_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&ChromeAppViewAsh::OnDisplayFileOpenDialog,
+ base::Unretained(app_view_), title, filter,
+ default_path, allow_multiple_files));
+ }
+
+ void OnDisplayFileSaveAsDialog(
+ const MetroViewerHostMsg_SaveAsDialogParams& params) {
+ ui_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&ChromeAppViewAsh::OnDisplayFileSaveAsDialog,
+ base::Unretained(app_view_), params));
+ }
+
+ void OnDisplayFolderPicker(const base::string16& title) {
+ ui_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&ChromeAppViewAsh::OnDisplayFolderPicker,
+ base::Unretained(app_view_), title));
+ }
+
+ void OnSetCursorPos(int x, int y) {
+ VLOG(1) << "In IPC OnSetCursorPos: " << x << ", " << y;
+ ui_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&ChromeAppViewAsh::OnSetCursorPos,
+ base::Unretained(app_view_), x, y));
+ }
+
+ void OnImeCancelComposition() {
+ ui_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&ChromeAppViewAsh::OnImeCancelComposition,
+ base::Unretained(app_view_)));
+ }
+
+ void OnImeTextInputClientChanged(
+ const std::vector<int32_t>& input_scopes,
+ const std::vector<metro_viewer::CharacterBounds>& character_bounds) {
+ ui_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&ChromeAppViewAsh::OnImeUpdateTextInputClient,
+ base::Unretained(app_view_), input_scopes,
+ character_bounds));
+ }
+
+ scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
+ ChromeAppViewAsh* app_view_;
+};
+
+void RunMessageLoop(winui::Core::ICoreDispatcher* dispatcher) {
+ // We're entering a nested message loop, let's allow dispatching
+ // tasks while we're in there.
+ base::MessageLoop::current()->SetNestableTasksAllowed(true);
+
+ // Enter main core message loop. There are several ways to exit it
+ // Nicely:
+ // 1 - User action like ALT-F4.
+ // 2 - Calling ICoreApplicationExit::Exit().
+ // 3- Posting WM_CLOSE to the core window.
+ dispatcher->ProcessEvents(
+ winui::Core::CoreProcessEventsOption
+ ::CoreProcessEventsOption_ProcessUntilQuit);
+
+ // Wind down the thread's chrome message loop.
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
+// Helper to return the state of the shift/control/alt keys.
+uint32_t GetKeyboardEventFlags() {
+ uint32_t flags = 0;
+ if (ui::win::IsShiftPressed())
+ flags |= ui::EF_SHIFT_DOWN;
+ if (ui::win::IsCtrlPressed())
+ flags |= ui::EF_CONTROL_DOWN;
+ if (ui::win::IsAltPressed())
+ flags |= ui::EF_ALT_DOWN;
+ return flags;
+}
+
+bool LaunchChromeBrowserProcess(const wchar_t* additional_parameters,
+ winapp::Activation::IActivatedEventArgs* args) {
+ if (args) {
+ DVLOG(1) << __FUNCTION__ << ":" << ::GetCommandLineW();
+ winapp::Activation::ActivationKind activation_kind;
+ CheckHR(args->get_Kind(&activation_kind));
+
+ DVLOG(1) << __FUNCTION__ << ", activation_kind=" << activation_kind;
+
+ if (activation_kind == winapp::Activation::ActivationKind_Launch) {
+ mswr::ComPtr<winapp::Activation::ILaunchActivatedEventArgs> launch_args;
+ if (args->QueryInterface(
+ winapp::Activation::IID_ILaunchActivatedEventArgs,
+ &launch_args) == S_OK) {
+ DVLOG(1) << "Activate: ActivationKind_Launch";
+ mswrw::HString launch_args_str;
+ launch_args->get_Arguments(launch_args_str.GetAddressOf());
+ base::string16 actual_launch_args(
+ MakeStdWString(launch_args_str.Get()));
+ if (actual_launch_args == win8::kMetroViewerConnectVerb) {
+ DVLOG(1) << __FUNCTION__ << "Not launching chrome server";
+ return true;
+ }
+ }
+ }
+ }
+
+ DVLOG(1) << "Launching chrome server";
+ base::FilePath chrome_exe_path;
+
+ if (!PathService::Get(base::FILE_EXE, &chrome_exe_path))
+ return false;
+
+ base::string16 parameters = L"--silent-launch --connect-to-metro-viewer ";
+ if (additional_parameters)
+ parameters += additional_parameters;
+
+ SHELLEXECUTEINFO sei = { sizeof(sei) };
+ sei.nShow = SW_SHOWNORMAL;
+ sei.lpFile = chrome_exe_path.value().c_str();
+ sei.lpDirectory = L"";
+ sei.lpParameters = parameters.c_str();
+ ::ShellExecuteEx(&sei);
+ return true;
+}
+
+} // namespace
+
+// This class helps decoding the pointer properties of an event.
+class ChromeAppViewAsh::PointerInfoHandler {
+ public:
+ PointerInfoHandler(float metro_dpi_scale, float win32_dpi_scale)
+ : x_(0),
+ y_(0),
+ wheel_delta_(0),
+ pointer_id_(0),
+ update_kind_(winui::Input::PointerUpdateKind_Other),
+ timestamp_(0),
+ mouse_down_flags_(0),
+ is_horizontal_wheel_(0),
+ metro_dpi_scale_(metro_dpi_scale),
+ win32_dpi_scale_(win32_dpi_scale) {}
+
+ HRESULT Init(winui::Core::IPointerEventArgs* args) {
+ HRESULT hr = args->get_CurrentPoint(&pointer_point_);
+ if (FAILED(hr))
+ return hr;
+
+ winfoundtn::Point point;
+ hr = pointer_point_->get_Position(&point);
+ if (FAILED(hr))
+ return hr;
+
+ mswr::ComPtr<winui::Input::IPointerPointProperties> properties;
+ hr = pointer_point_->get_Properties(&properties);
+ if (FAILED(hr))
+ return hr;
+
+ hr = properties->get_PointerUpdateKind(&update_kind_);
+ if (FAILED(hr))
+ return hr;
+
+ hr = properties->get_MouseWheelDelta(&wheel_delta_);
+ if (FAILED(hr))
+ return hr;
+
+ is_horizontal_wheel_ = 0;
+ properties->get_IsHorizontalMouseWheel(&is_horizontal_wheel_);
+
+ // The input coordinates are in DIP based on the metro scale factor.
+ // We want to convert it to DIP based on the win32 scale factor.
+ // We scale the point by the metro scale factor and then scale down
+ // via the win32 scale factor which achieves the needful.
+ gfx::Point dip_point_metro(point.X, point.Y);
+ gfx::Point scaled_point_metro =
+ gfx::ScaleToCeiledPoint(dip_point_metro, metro_dpi_scale_);
+ gfx::Point dip_point_win32 =
+ gfx::ScaleToCeiledPoint(scaled_point_metro, 1.0 / win32_dpi_scale_);
+ x_ = dip_point_win32.x();
+ y_ = dip_point_win32.y();
+
+ pointer_point_->get_Timestamp(&timestamp_);
+ pointer_point_->get_PointerId(&pointer_id_);
+ // Map the OS touch event id to a range allowed by the gesture recognizer.
+ if (IsTouch())
+ pointer_id_ %= ui::MotionEvent::MAX_TOUCH_POINT_COUNT;
+
+ boolean left_button_state;
+ hr = properties->get_IsLeftButtonPressed(&left_button_state);
+ if (FAILED(hr))
+ return hr;
+ if (left_button_state)
+ mouse_down_flags_ |= ui::EF_LEFT_MOUSE_BUTTON;
+
+ boolean right_button_state;
+ hr = properties->get_IsRightButtonPressed(&right_button_state);
+ if (FAILED(hr))
+ return hr;
+ if (right_button_state)
+ mouse_down_flags_ |= ui::EF_RIGHT_MOUSE_BUTTON;
+
+ boolean middle_button_state;
+ hr = properties->get_IsMiddleButtonPressed(&middle_button_state);
+ if (FAILED(hr))
+ return hr;
+ if (middle_button_state)
+ mouse_down_flags_ |= ui::EF_MIDDLE_MOUSE_BUTTON;
+
+ return S_OK;
+ }
+
+ bool IsType(windevs::Input::PointerDeviceType type) const {
+ mswr::ComPtr<windevs::Input::IPointerDevice> pointer_device;
+ CheckHR(pointer_point_->get_PointerDevice(&pointer_device));
+ windevs::Input::PointerDeviceType device_type;
+ CheckHR(pointer_device->get_PointerDeviceType(&device_type));
+ return (device_type == type);
+ }
+
+ bool IsMouse() const {
+ return IsType(windevs::Input::PointerDeviceType_Mouse);
+ }
+
+ bool IsTouch() const {
+ return IsType(windevs::Input::PointerDeviceType_Touch);
+ }
+
+ int32_t wheel_delta() const { return wheel_delta_; }
+
+ // Identifies the button that changed.
+ ui::EventFlags changed_button() const {
+ switch (update_kind_) {
+ case winui::Input::PointerUpdateKind_LeftButtonPressed:
+ return ui::EF_LEFT_MOUSE_BUTTON;
+ case winui::Input::PointerUpdateKind_LeftButtonReleased:
+ return ui::EF_LEFT_MOUSE_BUTTON;
+ case winui::Input::PointerUpdateKind_RightButtonPressed:
+ return ui::EF_RIGHT_MOUSE_BUTTON;
+ case winui::Input::PointerUpdateKind_RightButtonReleased:
+ return ui::EF_RIGHT_MOUSE_BUTTON;
+ case winui::Input::PointerUpdateKind_MiddleButtonPressed:
+ return ui::EF_MIDDLE_MOUSE_BUTTON;
+ case winui::Input::PointerUpdateKind_MiddleButtonReleased:
+ return ui::EF_MIDDLE_MOUSE_BUTTON;
+ default:
+ return ui::EF_NONE;
+ }
+ }
+
+ uint32_t mouse_down_flags() const { return mouse_down_flags_; }
+
+ int x() const { return x_; }
+ int y() const { return y_; }
+
+ uint32_t pointer_id() const { return pointer_id_; }
+
+ uint64_t timestamp() const { return timestamp_; }
+
+ winui::Input::PointerUpdateKind update_kind() const { return update_kind_; }
+
+ bool is_horizontal_wheel() const { return !!is_horizontal_wheel_; }
+
+ private:
+ int x_;
+ int y_;
+ int wheel_delta_;
+ uint32_t pointer_id_;
+ winui::Input::PointerUpdateKind update_kind_;
+ mswr::ComPtr<winui::Input::IPointerPoint> pointer_point_;
+ uint64_t timestamp_;
+
+ // Bitmask of ui::EventFlags corresponding to the buttons that are currently
+ // down.
+ uint32_t mouse_down_flags_;
+
+ // Set to true for a horizontal wheel message.
+ boolean is_horizontal_wheel_;
+
+ // The metro device scale factor as reported by the winrt interfaces.
+ float metro_dpi_scale_;
+ // The win32 dpi scale which is queried via GetDeviceCaps. Please refer to
+ // ui/gfx/win/dpi.cc for more information.
+ float win32_dpi_scale_;
+
+ DISALLOW_COPY_AND_ASSIGN(PointerInfoHandler);
+};
+
+ChromeAppViewAsh::ChromeAppViewAsh()
+ : mouse_down_flags_(ui::EF_NONE),
+ ui_channel_(nullptr),
+ core_window_hwnd_(NULL),
+ metro_dpi_scale_(0),
+ win32_dpi_scale_(0),
+ last_cursor_(NULL),
+ channel_listener_(NULL) {
+ DVLOG(1) << __FUNCTION__;
+ globals.previous_state =
+ winapp::Activation::ApplicationExecutionState_NotRunning;
+}
+
+ChromeAppViewAsh::~ChromeAppViewAsh() {
+ DVLOG(1) << __FUNCTION__;
+}
+
+IFACEMETHODIMP
+ChromeAppViewAsh::Initialize(winapp::Core::ICoreApplicationView* view) {
+ view_ = view;
+ DVLOG(1) << __FUNCTION__;
+ HRESULT hr = view_->add_Activated(mswr::Callback<ActivatedHandler>(
+ this, &ChromeAppViewAsh::OnActivate).Get(),
+ &activated_token_);
+ CheckHR(hr);
+ return hr;
+}
+
+IFACEMETHODIMP
+ChromeAppViewAsh::SetWindow(winui::Core::ICoreWindow* window) {
+ window_ = window;
+ DVLOG(1) << __FUNCTION__;
+
+ // Retrieve the native window handle via the interop layer.
+ mswr::ComPtr<ICoreWindowInterop> interop;
+ HRESULT hr = window->QueryInterface(interop.GetAddressOf());
+ CheckHR(hr);
+ hr = interop->get_WindowHandle(&core_window_hwnd_);
+ CheckHR(hr);
+
+ text_service_ = metro_driver::CreateTextService(this, core_window_hwnd_);
+
+ hr = window_->add_SizeChanged(mswr::Callback<SizeChangedHandler>(
+ this, &ChromeAppViewAsh::OnSizeChanged).Get(),
+ &sizechange_token_);
+ CheckHR(hr);
+
+ // Register for pointer and keyboard notifications. We forward
+ // them to the browser process via IPC.
+ hr = window_->add_PointerMoved(mswr::Callback<PointerEventHandler>(
+ this, &ChromeAppViewAsh::OnPointerMoved).Get(),
+ &pointermoved_token_);
+ CheckHR(hr);
+
+ hr = window_->add_PointerPressed(mswr::Callback<PointerEventHandler>(
+ this, &ChromeAppViewAsh::OnPointerPressed).Get(),
+ &pointerpressed_token_);
+ CheckHR(hr);
+
+ hr = window_->add_PointerReleased(mswr::Callback<PointerEventHandler>(
+ this, &ChromeAppViewAsh::OnPointerReleased).Get(),
+ &pointerreleased_token_);
+ CheckHR(hr);
+
+ hr = window_->add_KeyDown(mswr::Callback<KeyEventHandler>(
+ this, &ChromeAppViewAsh::OnKeyDown).Get(),
+ &keydown_token_);
+ CheckHR(hr);
+
+ hr = window_->add_KeyUp(mswr::Callback<KeyEventHandler>(
+ this, &ChromeAppViewAsh::OnKeyUp).Get(),
+ &keyup_token_);
+ CheckHR(hr);
+
+ mswr::ComPtr<winui::Core::ICoreDispatcher> dispatcher;
+ hr = window_->get_Dispatcher(dispatcher.GetAddressOf());
+ CheckHR(hr, "Get Dispatcher failed.");
+
+ mswr::ComPtr<winui::Core::ICoreAcceleratorKeys> accelerator_keys;
+ hr = dispatcher.CopyTo(__uuidof(winui::Core::ICoreAcceleratorKeys),
+ reinterpret_cast<void**>(
+ accelerator_keys.GetAddressOf()));
+ CheckHR(hr, "QI for ICoreAcceleratorKeys failed.");
+ hr = accelerator_keys->add_AcceleratorKeyActivated(
+ mswr::Callback<AcceleratorKeyEventHandler>(
+ this, &ChromeAppViewAsh::OnAcceleratorKeyDown).Get(),
+ &accel_keydown_token_);
+ CheckHR(hr);
+
+ hr = window_->add_PointerWheelChanged(mswr::Callback<PointerEventHandler>(
+ this, &ChromeAppViewAsh::OnWheel).Get(),
+ &wheel_token_);
+ CheckHR(hr);
+
+ hr = window_->add_CharacterReceived(mswr::Callback<CharEventHandler>(
+ this, &ChromeAppViewAsh::OnCharacterReceived).Get(),
+ &character_received_token_);
+ CheckHR(hr);
+
+ hr = window_->add_Activated(mswr::Callback<WindowActivatedHandler>(
+ this, &ChromeAppViewAsh::OnWindowActivated).Get(),
+ &window_activated_token_);
+ CheckHR(hr);
+
+ if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
+ // Register for edge gesture notifications only for Windows 8 and above.
+ mswr::ComPtr<winui::Input::IEdgeGestureStatics> edge_gesture_statics;
+ hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_UI_Input_EdgeGesture,
+ edge_gesture_statics.GetAddressOf());
+ CheckHR(hr);
+
+ mswr::ComPtr<winui::Input::IEdgeGesture> edge_gesture;
+ hr = edge_gesture_statics->GetForCurrentView(&edge_gesture);
+ CheckHR(hr);
+
+ hr = edge_gesture->add_Completed(mswr::Callback<EdgeEventHandler>(
+ this, &ChromeAppViewAsh::OnEdgeGestureCompleted).Get(),
+ &edgeevent_token_);
+ CheckHR(hr);
+ }
+
+ // By initializing the direct 3D swap chain with the corewindow
+ // we can now directly blit to it from the browser process.
+ direct3d_helper_.Initialize(window);
+ DVLOG(1) << "Initialized Direct3D.";
+
+ // On Windows 8+ the WinRT interface IDisplayProperties which we use to get
+ // device scale factor does not return the correct values in metro mode.
+ // To workaround this we retrieve the device scale factor via the win32 way
+ // and scale input coordinates accordingly to pass them in DIP to chrome.
+ // TODO(ananta). Investigate and fix.
+ metro_dpi_scale_ = GetModernUIScale();
+ win32_dpi_scale_ = gfx::GetDPIScale();
+ DVLOG(1) << "Metro Scale is " << metro_dpi_scale_;
+ DVLOG(1) << "Win32 Scale is " << win32_dpi_scale_;
+ return S_OK;
+}
+
+IFACEMETHODIMP
+ChromeAppViewAsh::Load(HSTRING entryPoint) {
+ // On Win7 |entryPoint| is NULL.
+ DVLOG(1) << __FUNCTION__;
+ return S_OK;
+}
+
+IFACEMETHODIMP
+ChromeAppViewAsh::Run() {
+ DVLOG(1) << __FUNCTION__;
+ mswr::ComPtr<winui::Core::ICoreDispatcher> dispatcher;
+ HRESULT hr = window_->get_Dispatcher(dispatcher.GetAddressOf());
+ CheckHR(hr, "Dispatcher failed.");
+
+ // Create the IPC channel IO thread. It needs to out-live the ChannelProxy.
+ io_thread_.reset(new base::Thread("metro_IO_thread"));
+ base::Thread::Options options;
+ options.message_loop_type = base::MessageLoop::TYPE_IO;
+ io_thread_->StartWithOptions(options);
+
+ ChromeChannelListener ui_channel_listener(&ui_loop_, this);
+ channel_listener_ = &ui_channel_listener;
+
+ // We can't do anything until the Chrome browser IPC channel is initialized.
+ // Lazy initialization in a timer.
+ ui_loop_.PostDelayedTask(FROM_HERE,
+ base::Bind(base::IgnoreResult(&ChromeAppViewAsh::StartChromeOSMode),
+ base::Unretained(this)),
+ base::TimeDelta::FromMilliseconds(kChromeChannelPollTimerMs));
+
+ // Post the task that'll do the inner Metro message pumping to it.
+ ui_loop_.PostTask(FROM_HERE, base::Bind(&RunMessageLoop, dispatcher.Get()));
+ ui_loop_.Run();
+
+ io_thread_.reset(NULL);
+ ui_channel_.reset(NULL);
+ channel_listener_ = NULL;
+
+ DVLOG(0) << "ProcessEvents done, hr=" << hr;
+ return hr;
+}
+
+IFACEMETHODIMP
+ChromeAppViewAsh::Uninitialize() {
+ DVLOG(1) << __FUNCTION__;
+ metro_driver::RemoveImePopupObserver(this);
+ input_source_.reset();
+ text_service_.reset();
+ window_ = nullptr;
+ view_ = nullptr;
+ core_window_hwnd_ = NULL;
+ return S_OK;
+}
+
+// static
+HRESULT ChromeAppViewAsh::Unsnap() {
+ mswr::ComPtr<winui::ViewManagement::IApplicationViewStatics> view_statics;
+ HRESULT hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_UI_ViewManagement_ApplicationView,
+ view_statics.GetAddressOf());
+ CheckHR(hr);
+
+ winui::ViewManagement::ApplicationViewState state =
+ winui::ViewManagement::ApplicationViewState_FullScreenLandscape;
+ hr = view_statics->get_Value(&state);
+ CheckHR(hr);
+
+ if (state == winui::ViewManagement::ApplicationViewState_Snapped) {
+ boolean success = FALSE;
+ hr = view_statics->TryUnsnap(&success);
+
+ if (FAILED(hr) || !success) {
+ LOG(ERROR) << "Failed to unsnap. Error 0x" << hr;
+ if (SUCCEEDED(hr))
+ hr = E_UNEXPECTED;
+ }
+ }
+ return hr;
+}
+
+void ChromeAppViewAsh::OnActivateDesktop(const base::FilePath& file_path,
+ bool ash_exit) {
+ DVLOG(1) << "ChannelAppViewAsh::OnActivateDesktop\n";
+
+ if (ash_exit) {
+ // As we are the top level window, the exiting is done async so we manage
+ // to execute the entire function including the final Send().
+ OnMetroExit(TERMINATE_USING_KEY_SEQUENCE);
+ }
+
+ // We are just executing delegate_execute here without parameters. Assumption
+ // here is that this process will be reused by shell when asking for
+ // IExecuteCommand interface.
+
+ // TODO(shrikant): Consolidate ShellExecuteEx with SEE_MASK_FLAG_LOG_USAGE
+ // and place it metro.h or similar accessible file from all code code paths
+ // using this function.
+ SHELLEXECUTEINFO sei = { sizeof(sei) };
+ sei.fMask = SEE_MASK_FLAG_LOG_USAGE;
+ sei.nShow = SW_SHOWNORMAL;
+ sei.lpFile = file_path.value().c_str();
+ sei.lpParameters = NULL;
+ if (!ash_exit)
+ sei.fMask |= SEE_MASK_NOCLOSEPROCESS;
+ ::ShellExecuteExW(&sei);
+ if (!ash_exit) {
+ ::TerminateProcess(sei.hProcess, 0);
+ ::CloseHandle(sei.hProcess);
+ }
+}
+
+void ChromeAppViewAsh::OnOpenURLOnDesktop(const base::FilePath& shortcut,
+ const base::string16& url) {
+ base::FilePath::StringType file = shortcut.value();
+ SHELLEXECUTEINFO sei = { sizeof(sei) };
+ sei.fMask = SEE_MASK_FLAG_LOG_USAGE;
+ sei.nShow = SW_SHOWNORMAL;
+ sei.lpFile = file.c_str();
+ sei.lpDirectory = L"";
+ sei.lpParameters = url.c_str();
+ ShellExecuteEx(&sei);
+}
+
+void ChromeAppViewAsh::OnSetCursor(HCURSOR cursor) {
+ ::SetCursor(cursor);
+ last_cursor_ = cursor;
+}
+
+void ChromeAppViewAsh::OnDisplayFileOpenDialog(
+ const base::string16& title,
+ const base::string16& filter,
+ const base::FilePath& default_path,
+ bool allow_multiple_files) {
+ DVLOG(1) << __FUNCTION__;
+
+ // The OpenFilePickerSession instance is deleted when we receive a
+ // callback from the OpenFilePickerSession class about the completion of the
+ // operation.
+ FilePickerSessionBase* file_picker_ =
+ new OpenFilePickerSession(this,
+ title,
+ filter,
+ default_path,
+ allow_multiple_files);
+ file_picker_->Run();
+}
+
+void ChromeAppViewAsh::OnDisplayFileSaveAsDialog(
+ const MetroViewerHostMsg_SaveAsDialogParams& params) {
+ DVLOG(1) << __FUNCTION__;
+
+ // The SaveFilePickerSession instance is deleted when we receive a
+ // callback from the SaveFilePickerSession class about the completion of the
+ // operation.
+ FilePickerSessionBase* file_picker_ =
+ new SaveFilePickerSession(this, params);
+ file_picker_->Run();
+}
+
+void ChromeAppViewAsh::OnDisplayFolderPicker(const base::string16& title) {
+ DVLOG(1) << __FUNCTION__;
+ // The FolderPickerSession instance is deleted when we receive a
+ // callback from the FolderPickerSession class about the completion of the
+ // operation.
+ FilePickerSessionBase* file_picker_ = new FolderPickerSession(this, title);
+ file_picker_->Run();
+}
+
+void ChromeAppViewAsh::OnSetCursorPos(int x, int y) {
+ if (ui_channel_) {
+ ::SetCursorPos(x, y);
+ DVLOG(1) << "In UI OnSetCursorPos: " << x << ", " << y;
+ ui_channel_->Send(new MetroViewerHostMsg_SetCursorPosAck());
+ // Generate a fake mouse move which matches the SetCursor coordinates as
+ // the browser expects to receive a mouse move for these coordinates.
+ // It is not clear why we don't receive a real mouse move in response to
+ // the SetCursorPos calll above.
+ ui_channel_->Send(new MetroViewerHostMsg_MouseMoved(x, y, 0));
+ }
+}
+
+void ChromeAppViewAsh::OnOpenFileCompleted(
+ OpenFilePickerSession* open_file_picker,
+ bool success) {
+ DVLOG(1) << __FUNCTION__;
+ DVLOG(1) << "Success: " << success;
+ if (ui_channel_) {
+ if (open_file_picker->allow_multi_select()) {
+ ui_channel_->Send(new MetroViewerHostMsg_MultiFileOpenDone(
+ success, open_file_picker->filenames()));
+ } else {
+ ui_channel_->Send(new MetroViewerHostMsg_FileOpenDone(
+ success, base::FilePath(open_file_picker->result())));
+ }
+ }
+ delete open_file_picker;
+}
+
+void ChromeAppViewAsh::OnSaveFileCompleted(
+ SaveFilePickerSession* save_file_picker,
+ bool success) {
+ DVLOG(1) << __FUNCTION__;
+ DVLOG(1) << "Success: " << success;
+ if (ui_channel_) {
+ ui_channel_->Send(new MetroViewerHostMsg_FileSaveAsDone(
+ success,
+ base::FilePath(save_file_picker->result()),
+ save_file_picker->filter_index()));
+ }
+ delete save_file_picker;
+}
+
+void ChromeAppViewAsh::OnFolderPickerCompleted(
+ FolderPickerSession* folder_picker,
+ bool success) {
+ DVLOG(1) << __FUNCTION__;
+ DVLOG(1) << "Success: " << success;
+ if (ui_channel_) {
+ ui_channel_->Send(new MetroViewerHostMsg_SelectFolderDone(
+ success,
+ base::FilePath(folder_picker->result())));
+ }
+ delete folder_picker;
+}
+
+void ChromeAppViewAsh::OnImeCancelComposition() {
+ if (!text_service_)
+ return;
+ text_service_->CancelComposition();
+}
+
+void ChromeAppViewAsh::OnImeUpdateTextInputClient(
+ const std::vector<int32_t>& input_scopes,
+ const std::vector<metro_viewer::CharacterBounds>& character_bounds) {
+ if (!text_service_)
+ return;
+ text_service_->OnDocumentChanged(input_scopes, character_bounds);
+}
+
+void ChromeAppViewAsh::OnImePopupChanged(ImePopupObserver::EventType event) {
+ if (!ui_channel_)
+ return;
+ switch (event) {
+ case ImePopupObserver::kPopupShown:
+ ui_channel_->Send(new MetroViewerHostMsg_ImeCandidatePopupChanged(true));
+ return;
+ case ImePopupObserver::kPopupHidden:
+ ui_channel_->Send(new MetroViewerHostMsg_ImeCandidatePopupChanged(false));
+ return;
+ case ImePopupObserver::kPopupUpdated:
+ // TODO(kochi): Support this event for W3C IME API proposal.
+ // See crbug.com/238585.
+ return;
+ default:
+ NOTREACHED() << "unknown event type: " << event;
+ return;
+ }
+}
+
+// Function to Exit metro chrome cleanly. If we are in the foreground
+// then we try and exit by sending an Alt+F4 key combination to the core
+// window which ensures that the chrome application tile does not show up in
+// the running metro apps list on the top left corner.
+void ChromeAppViewAsh::OnMetroExit(MetroTerminateMethod method) {
+ if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
+ HWND core_window = core_window_hwnd();
+ if (method == TERMINATE_USING_KEY_SEQUENCE && core_window != NULL &&
+ core_window == ::GetForegroundWindow()) {
+ DVLOG(1) << "We are in the foreground. Exiting via Alt F4";
+ SendKeySequence(VK_F4, ALT);
+ if (ui_channel_)
+ ui_channel_->Close();
+ } else {
+ globals.app_exit->Exit();
+ }
+ } else {
+ if (ui_channel_)
+ ui_channel_->Close();
+
+ HWND core_window = core_window_hwnd();
+ ::PostMessage(core_window, WM_CLOSE, 0, 0);
+
+ globals.app_exit->Exit();
+ }
+}
+
+void ChromeAppViewAsh::OnInputSourceChanged() {
+ if (!input_source_)
+ return;
+
+ DCHECK(ui_channel_);
+
+ LANGID langid = 0;
+ bool is_ime = false;
+ if (!input_source_->GetActiveSource(&langid, &is_ime)) {
+ LOG(ERROR) << "GetActiveSource failed";
+ return;
+ }
+ ui_channel_->Send(new MetroViewerHostMsg_ImeInputSourceChanged(langid,
+ is_ime));
+}
+
+void ChromeAppViewAsh::OnCompositionChanged(
+ const base::string16& text,
+ int32_t selection_start,
+ int32_t selection_end,
+ const std::vector<metro_viewer::UnderlineInfo>& underlines) {
+ ui_channel_->Send(new MetroViewerHostMsg_ImeCompositionChanged(
+ text, selection_start, selection_end, underlines));
+}
+
+void ChromeAppViewAsh::OnTextCommitted(const base::string16& text) {
+ ui_channel_->Send(new MetroViewerHostMsg_ImeTextCommitted(text));
+}
+
+void ChromeAppViewAsh::SendMouseButton(int x,
+ int y,
+ int extra,
+ ui::EventType event_type,
+ uint32_t flags,
+ ui::EventFlags changed_button,
+ bool is_horizontal_wheel) {
+ if (!ui_channel_)
+ return;
+ MetroViewerHostMsg_MouseButtonParams params;
+ params.x = static_cast<int32_t>(x);
+ params.y = static_cast<int32_t>(y);
+ params.extra = static_cast<int32_t>(extra);
+ params.event_type = event_type;
+ params.flags = static_cast<int32_t>(flags);
+ params.changed_button = changed_button;
+ params.is_horizontal_wheel = is_horizontal_wheel;
+ ui_channel_->Send(new MetroViewerHostMsg_MouseButton(params));
+}
+
+void ChromeAppViewAsh::GenerateMouseEventFromMoveIfNecessary(
+ const PointerInfoHandler& pointer) {
+ ui::EventType event_type;
+ // For aura we want the flags to include the button that was released, thus
+ // we or the old and new.
+ uint32_t mouse_down_flags = pointer.mouse_down_flags() | mouse_down_flags_;
+ mouse_down_flags_ = pointer.mouse_down_flags();
+ switch (pointer.update_kind()) {
+ case winui::Input::PointerUpdateKind_LeftButtonPressed:
+ case winui::Input::PointerUpdateKind_RightButtonPressed:
+ case winui::Input::PointerUpdateKind_MiddleButtonPressed:
+ event_type = ui::ET_MOUSE_PRESSED;
+ break;
+ case winui::Input::PointerUpdateKind_LeftButtonReleased:
+ case winui::Input::PointerUpdateKind_RightButtonReleased:
+ case winui::Input::PointerUpdateKind_MiddleButtonReleased:
+ event_type = ui::ET_MOUSE_RELEASED;
+ break;
+ default:
+ return;
+ }
+ SendMouseButton(pointer.x(), pointer.y(), 0, event_type,
+ mouse_down_flags | GetKeyboardEventFlags(),
+ pointer.changed_button(), pointer.is_horizontal_wheel());
+}
+
+HRESULT ChromeAppViewAsh::OnActivate(
+ winapp::Core::ICoreApplicationView*,
+ winapp::Activation::IActivatedEventArgs* args) {
+ DVLOG(1) << __FUNCTION__;
+ // Note: If doing more work in this function, you migth need to call
+ // get_PreviousExecutionState() and skip the work if the result is
+ // ApplicationExecutionState_Running and globals.previous_state is too.
+ args->get_PreviousExecutionState(&globals.previous_state);
+ DVLOG(1) << "Previous Execution State: " << globals.previous_state;
+
+ winapp::Activation::ActivationKind activation_kind;
+ CheckHR(args->get_Kind(&activation_kind));
+ DVLOG(1) << "Activation kind: " << activation_kind;
+
+ if (activation_kind == winapp::Activation::ActivationKind_Search)
+ HandleSearchRequest(args);
+ else if (activation_kind == winapp::Activation::ActivationKind_Protocol)
+ HandleProtocolRequest(args);
+ else
+ LaunchChromeBrowserProcess(NULL, args);
+ // We call ICoreWindow::Activate after the handling for the search/protocol
+ // requests because Chrome can be launched to handle a search request which
+ // in turn launches the chrome browser process in desktop mode via
+ // ShellExecute. If we call ICoreWindow::Activate before this, then
+ // Windows kills the metro chrome process when it calls ShellExecute. Seems
+ // to be a bug.
+ window_->Activate();
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::OnPointerMoved(winui::Core::ICoreWindow* sender,
+ winui::Core::IPointerEventArgs* args) {
+ if (!ui_channel_)
+ return S_OK;
+
+ PointerInfoHandler pointer(metro_dpi_scale_, win32_dpi_scale_);
+ HRESULT hr = pointer.Init(args);
+ if (FAILED(hr))
+ return hr;
+
+ if (pointer.IsMouse()) {
+ // If the mouse was moved towards the charms or the OS specific section,
+ // the cursor may change from what the browser last set. Restore it here.
+ if (::GetCursor() != last_cursor_)
+ SetCursor(last_cursor_);
+
+ GenerateMouseEventFromMoveIfNecessary(pointer);
+ ui_channel_->Send(new MetroViewerHostMsg_MouseMoved(
+ pointer.x(),
+ pointer.y(),
+ mouse_down_flags_ | GetKeyboardEventFlags()));
+ } else {
+ DCHECK(pointer.IsTouch());
+ ui_channel_->Send(new MetroViewerHostMsg_TouchMoved(pointer.x(),
+ pointer.y(),
+ pointer.timestamp(),
+ pointer.pointer_id()));
+ }
+ return S_OK;
+}
+
+// NOTE: From experimentation, it seems like Metro only sends a PointerPressed
+// event for the first button pressed and the last button released in a sequence
+// of mouse events.
+// For example, a sequence of LEFT_DOWN, RIGHT_DOWN, LEFT_UP, RIGHT_UP results
+// only in PointerPressed(LEFT)/PointerReleased(RIGHT) events. Intermediary
+// presses and releases are tracked in OnPointMoved().
+HRESULT ChromeAppViewAsh::OnPointerPressed(
+ winui::Core::ICoreWindow* sender,
+ winui::Core::IPointerEventArgs* args) {
+ if (!ui_channel_)
+ return S_OK;
+
+ PointerInfoHandler pointer(metro_dpi_scale_, win32_dpi_scale_);
+ HRESULT hr = pointer.Init(args);
+ if (FAILED(hr))
+ return hr;
+
+ if (pointer.IsMouse()) {
+ mouse_down_flags_ = pointer.mouse_down_flags();
+ SendMouseButton(pointer.x(), pointer.y(), 0, ui::ET_MOUSE_PRESSED,
+ mouse_down_flags_ | GetKeyboardEventFlags(),
+ pointer.changed_button(), pointer.is_horizontal_wheel());
+ } else {
+ DCHECK(pointer.IsTouch());
+ ui_channel_->Send(new MetroViewerHostMsg_TouchDown(pointer.x(),
+ pointer.y(),
+ pointer.timestamp(),
+ pointer.pointer_id()));
+ }
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::OnPointerReleased(
+ winui::Core::ICoreWindow* sender,
+ winui::Core::IPointerEventArgs* args) {
+ if (!ui_channel_)
+ return S_OK;
+
+ PointerInfoHandler pointer(metro_dpi_scale_, win32_dpi_scale_);
+ HRESULT hr = pointer.Init(args);
+ if (FAILED(hr))
+ return hr;
+
+ if (pointer.IsMouse()) {
+ mouse_down_flags_ = ui::EF_NONE;
+ SendMouseButton(pointer.x(), pointer.y(), 0, ui::ET_MOUSE_RELEASED,
+ static_cast<uint32_t>(pointer.changed_button()) |
+ GetKeyboardEventFlags(),
+ pointer.changed_button(), pointer.is_horizontal_wheel());
+ } else {
+ DCHECK(pointer.IsTouch());
+ ui_channel_->Send(new MetroViewerHostMsg_TouchUp(pointer.x(),
+ pointer.y(),
+ pointer.timestamp(),
+ pointer.pointer_id()));
+ }
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::OnWheel(
+ winui::Core::ICoreWindow* sender,
+ winui::Core::IPointerEventArgs* args) {
+ if (!ui_channel_)
+ return S_OK;
+
+ PointerInfoHandler pointer(metro_dpi_scale_, win32_dpi_scale_);
+ HRESULT hr = pointer.Init(args);
+ if (FAILED(hr))
+ return hr;
+ DCHECK(pointer.IsMouse());
+ SendMouseButton(pointer.x(), pointer.y(), pointer.wheel_delta(),
+ ui::ET_MOUSEWHEEL, GetKeyboardEventFlags(), ui::EF_NONE,
+ pointer.is_horizontal_wheel());
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::OnKeyDown(
+ winui::Core::ICoreWindow* sender,
+ winui::Core::IKeyEventArgs* args) {
+ if (!ui_channel_)
+ return S_OK;
+
+ winsys::VirtualKey virtual_key;
+ HRESULT hr = args->get_VirtualKey(&virtual_key);
+ if (FAILED(hr))
+ return hr;
+ winui::Core::CorePhysicalKeyStatus status;
+ hr = args->get_KeyStatus(&status);
+ if (FAILED(hr))
+ return hr;
+
+ ui_channel_->Send(new MetroViewerHostMsg_KeyDown(virtual_key,
+ status.RepeatCount,
+ status.ScanCode,
+ GetKeyboardEventFlags()));
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::OnKeyUp(
+ winui::Core::ICoreWindow* sender,
+ winui::Core::IKeyEventArgs* args) {
+ if (!ui_channel_)
+ return S_OK;
+
+ winsys::VirtualKey virtual_key;
+ HRESULT hr = args->get_VirtualKey(&virtual_key);
+ if (FAILED(hr))
+ return hr;
+ winui::Core::CorePhysicalKeyStatus status;
+ hr = args->get_KeyStatus(&status);
+ if (FAILED(hr))
+ return hr;
+
+ ui_channel_->Send(new MetroViewerHostMsg_KeyUp(virtual_key,
+ status.RepeatCount,
+ status.ScanCode,
+ GetKeyboardEventFlags()));
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::OnAcceleratorKeyDown(
+ winui::Core::ICoreDispatcher* sender,
+ winui::Core::IAcceleratorKeyEventArgs* args) {
+ if (!ui_channel_)
+ return S_OK;
+
+ winsys::VirtualKey virtual_key;
+ HRESULT hr = args->get_VirtualKey(&virtual_key);
+ if (FAILED(hr))
+ return hr;
+ winui::Core::CorePhysicalKeyStatus status;
+ hr = args->get_KeyStatus(&status);
+ if (FAILED(hr))
+ return hr;
+
+ winui::Core::CoreAcceleratorKeyEventType event_type;
+ hr = args->get_EventType(&event_type);
+ if (FAILED(hr))
+ return hr;
+
+ uint32_t keyboard_flags = GetKeyboardEventFlags();
+
+ switch (event_type) {
+ case winui::Core::CoreAcceleratorKeyEventType_SystemCharacter:
+ ui_channel_->Send(new MetroViewerHostMsg_Character(virtual_key,
+ status.RepeatCount,
+ status.ScanCode,
+ keyboard_flags));
+ break;
+
+ case winui::Core::CoreAcceleratorKeyEventType_SystemKeyDown:
+ // Don't send the Alt + F4 combination to Chrome as this is intended to
+ // shut the metro environment down. Reason we check for Control here is
+ // Windows does not shutdown metro if Ctrl is pressed along with Alt F4.
+ // Other key combinations with Alt F4 shutdown metro.
+ if ((virtual_key == VK_F4) && ((keyboard_flags & ui::EF_ALT_DOWN) &&
+ !(keyboard_flags & ui::EF_CONTROL_DOWN)))
+ return S_OK;
+ // Don't send the EF_ALT_DOWN modifier along with the IPC message for
+ // the Alt or F10 key. The accelerator for VKEY_MENU is registered
+ // without modifiers in Chrome for historical reasons. Not sending the
+ // EF_ALT_DOWN modifier ensures that the accelerator is processed
+ // correctly.
+ if (virtual_key == winsys::VirtualKey_Menu)
+ keyboard_flags &= ~ui::EF_ALT_DOWN;
+ ui_channel_->Send(new MetroViewerHostMsg_KeyDown(virtual_key,
+ status.RepeatCount,
+ status.ScanCode,
+ keyboard_flags));
+ break;
+
+ case winui::Core::CoreAcceleratorKeyEventType_SystemKeyUp:
+ ui_channel_->Send(new MetroViewerHostMsg_KeyUp(virtual_key,
+ status.RepeatCount,
+ status.ScanCode,
+ keyboard_flags));
+ break;
+
+ default:
+ break;
+ }
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::OnCharacterReceived(
+ winui::Core::ICoreWindow* sender,
+ winui::Core::ICharacterReceivedEventArgs* args) {
+ if (!ui_channel_)
+ return S_OK;
+
+ unsigned int char_code = 0;
+ HRESULT hr = args->get_KeyCode(&char_code);
+ if (FAILED(hr))
+ return hr;
+
+ winui::Core::CorePhysicalKeyStatus status;
+ hr = args->get_KeyStatus(&status);
+ if (FAILED(hr))
+ return hr;
+
+ ui_channel_->Send(new MetroViewerHostMsg_Character(char_code,
+ status.RepeatCount,
+ status.ScanCode,
+ GetKeyboardEventFlags()));
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::OnWindowActivated(
+ winui::Core::ICoreWindow* sender,
+ winui::Core::IWindowActivatedEventArgs* args) {
+ if (!ui_channel_)
+ return S_OK;
+
+ if (args) {
+ winui::Core::CoreWindowActivationState state;
+ HRESULT hr = args->get_WindowActivationState(&state);
+ if (FAILED(hr))
+ return hr;
+
+ // Treat both full activation (Ash was reopened from the Start Screen or
+ // from any other Metro entry point in Windows) and pointer activation
+ // (user clicked back in Ash after using another app on another monitor)
+ // the same.
+ if (state == winui::Core::CoreWindowActivationState_CodeActivated ||
+ state == winui::Core::CoreWindowActivationState_PointerActivated) {
+ ui_channel_->Send(new MetroViewerHostMsg_WindowActivated(false));
+ }
+ } else {
+ // On Windows 7, we force a repaint when the window is activated.
+ ui_channel_->Send(new MetroViewerHostMsg_WindowActivated(true));
+ }
+ if (text_service_)
+ text_service_->OnWindowActivated();
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::HandleSearchRequest(
+ winapp::Activation::IActivatedEventArgs* args) {
+ mswr::ComPtr<winapp::Activation::ISearchActivatedEventArgs> search_args;
+ CheckHR(args->QueryInterface(
+ winapp::Activation::IID_ISearchActivatedEventArgs, &search_args));
+
+ if (!ui_channel_) {
+ DVLOG(1) << "Launched to handle search request";
+ LaunchChromeBrowserProcess(L"--windows8-search", args);
+ }
+
+ mswrw::HString search_string;
+ CheckHR(search_args->get_QueryText(search_string.GetAddressOf()));
+ base::string16 search_text(MakeStdWString(search_string.Get()));
+
+ ui_loop_.PostTask(FROM_HERE,
+ base::Bind(&ChromeAppViewAsh::OnSearchRequest,
+ base::Unretained(this),
+ search_text));
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::HandleProtocolRequest(
+ winapp::Activation::IActivatedEventArgs* args) {
+ DVLOG(1) << __FUNCTION__;
+ if (!ui_channel_)
+ DVLOG(1) << "Launched to handle url request";
+
+ mswr::ComPtr<winapp::Activation::IProtocolActivatedEventArgs>
+ protocol_args;
+ CheckHR(args->QueryInterface(
+ winapp::Activation::IID_IProtocolActivatedEventArgs,
+ &protocol_args));
+
+ mswr::ComPtr<winfoundtn::IUriRuntimeClass> uri;
+ protocol_args->get_Uri(&uri);
+ mswrw::HString url;
+ uri->get_AbsoluteUri(url.GetAddressOf());
+ base::string16 actual_url(MakeStdWString(url.Get()));
+ DVLOG(1) << "Received url request: " << actual_url;
+
+ ui_loop_.PostTask(FROM_HERE,
+ base::Bind(&ChromeAppViewAsh::OnNavigateToUrl,
+ base::Unretained(this),
+ actual_url));
+ return S_OK;
+}
+
+HRESULT ChromeAppViewAsh::OnEdgeGestureCompleted(
+ winui::Input::IEdgeGesture* gesture,
+ winui::Input::IEdgeGestureEventArgs* args) {
+ if (ui_channel_)
+ ui_channel_->Send(new MetroViewerHostMsg_EdgeGesture());
+ return S_OK;
+}
+
+void ChromeAppViewAsh::OnSearchRequest(const base::string16& search_string) {
+ if (ui_channel_)
+ ui_channel_->Send(new MetroViewerHostMsg_SearchRequest(search_string));
+}
+
+void ChromeAppViewAsh::OnNavigateToUrl(const base::string16& url) {
+ if (ui_channel_)
+ ui_channel_->Send(new MetroViewerHostMsg_OpenURL(url));
+}
+
+HRESULT ChromeAppViewAsh::OnSizeChanged(winui::Core::ICoreWindow* sender,
+ winui::Core::IWindowSizeChangedEventArgs* args) {
+ if (!window_ || !ui_channel_) {
+ return S_OK;
+ }
+
+ // winui::Core::IWindowSizeChangedEventArgs args->Size appears to return
+ // scaled values under HiDPI. We will instead use GetWindowRect() which
+ // should always return values in Pixels.
+ RECT rect = {0};
+ ::GetWindowRect(core_window_hwnd_, &rect);
+
+ uint32_t cx = static_cast<uint32_t>(rect.right - rect.left);
+ uint32_t cy = static_cast<uint32_t>(rect.bottom - rect.top);
+
+ DVLOG(1) << "Window size changed: width=" << cx << ", height=" << cy;
+ ui_channel_->Send(new MetroViewerHostMsg_WindowSizeChanged(cx, cy));
+ return S_OK;
+}
+
+void ChromeAppViewAsh::StartChromeOSMode() {
+ static int ms_elapsed = 0;
+
+ if (!IPC::Channel::IsNamedServerInitialized(
+ win8::kMetroViewerIPCChannelName) && ms_elapsed < 10000) {
+ ms_elapsed += 100;
+ ui_loop_.PostDelayedTask(FROM_HERE,
+ base::Bind(base::IgnoreResult(&ChromeAppViewAsh::StartChromeOSMode),
+ base::Unretained(this)),
+ base::TimeDelta::FromMilliseconds(kChromeChannelPollTimerMs));
+ return;
+ }
+
+ if (!IPC::Channel::IsNamedServerInitialized(
+ win8::kMetroViewerIPCChannelName)) {
+ DVLOG(1) << "Failed to connect to chrome channel : "
+ << win8::kMetroViewerIPCChannelName;
+ DVLOG(1) << "Exiting. Elapsed time :" << ms_elapsed;
+ PostMessage(core_window_hwnd_, WM_CLOSE, 0, 0);
+ return;
+ }
+
+ DVLOG(1) << "Found channel : " << win8::kMetroViewerIPCChannelName;
+
+ DCHECK(channel_listener_);
+
+ // In Aura mode we create an IPC channel to the browser, then ask it to
+ // connect to us.
+ ui_channel_ =
+ IPC::ChannelProxy::Create(win8::kMetroViewerIPCChannelName,
+ IPC::Channel::MODE_NAMED_CLIENT,
+ channel_listener_,
+ io_thread_->task_runner());
+ DVLOG(1) << "Created channel proxy";
+
+ // Upon receipt of the MetroViewerHostMsg_SetTargetSurface message the
+ // browser will use D3D from the browser process to present to our Window.
+ ui_channel_->Send(new MetroViewerHostMsg_SetTargetSurface(
+ gfx::NativeViewId(core_window_hwnd_),
+ win32_dpi_scale_));
+ DVLOG(1) << "ICoreWindow sent " << core_window_hwnd_;
+
+ // Send an initial size message so that the Ash root window host gets sized
+ // correctly.
+ RECT rect = {0};
+ ::GetWindowRect(core_window_hwnd_, &rect);
+ ui_channel_->Send(
+ new MetroViewerHostMsg_WindowSizeChanged(rect.right - rect.left,
+ rect.bottom - rect.top));
+
+ input_source_ = metro_driver::InputSource::Create();
+ if (input_source_) {
+ input_source_->AddObserver(this);
+ // Send an initial input source.
+ OnInputSourceChanged();
+ }
+
+ // Start receiving IME popup window notifications.
+ metro_driver::AddImePopupObserver(this);
+
+ DVLOG(1) << "Channel setup complete";
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ChromeAppViewFactory::ChromeAppViewFactory(
+ winapp::Core::ICoreApplication* icore_app) {
+ mswr::ComPtr<winapp::Core::ICoreApplication> core_app(icore_app);
+ mswr::ComPtr<winapp::Core::ICoreApplicationExit> app_exit;
+ CheckHR(core_app.As(&app_exit));
+ globals.app_exit = app_exit.Detach();
+}
+
+IFACEMETHODIMP
+ChromeAppViewFactory::CreateView(winapp::Core::IFrameworkView** view) {
+ *view = mswr::Make<ChromeAppViewAsh>().Detach();
+ return (*view) ? S_OK : E_OUTOFMEMORY;
+}
diff --git a/win8/metro_driver/chrome_app_view_ash.h b/win8/metro_driver/chrome_app_view_ash.h
new file mode 100644
index 0000000..ffe1d7e
--- /dev/null
+++ b/win8/metro_driver/chrome_app_view_ash.h
@@ -0,0 +1,262 @@
+// Copyright (c) 2012 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_METRO_DRIVER_CHROME_APP_VIEW_ASH_H_
+#define WIN8_METRO_DRIVER_CHROME_APP_VIEW_ASH_H_
+
+#include <stdint.h>
+#include <windows.applicationmodel.core.h>
+#include <windows.ui.core.h>
+#include <windows.ui.input.h>
+#include <windows.ui.viewmanagement.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string16.h"
+#include "base/threading/thread.h"
+#include "ipc/ipc_listener.h"
+#include "ui/events/event_constants.h"
+#include "win8/metro_driver/direct3d_helper.h"
+#include "win8/metro_driver/ime/ime_popup_observer.h"
+#include "win8/metro_driver/ime/input_source_observer.h"
+#include "win8/metro_driver/ime/text_service_delegate.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace IPC {
+class Listener;
+class ChannelProxy;
+}
+
+namespace metro_driver {
+class InputSource;
+class TextService;
+}
+
+namespace metro_viewer {
+struct CharacterBounds;
+struct UnderlineInfo;
+}
+
+class OpenFilePickerSession;
+class SaveFilePickerSession;
+class FolderPickerSession;
+class FilePickerSessionBase;
+
+struct MetroViewerHostMsg_SaveAsDialogParams;
+
+enum MetroTerminateMethod {
+ TERMINATE_USING_KEY_SEQUENCE = 1,
+ TERMINATE_USING_PROCESS_EXIT = 2,
+};
+
+class ChromeAppViewAsh
+ : public mswr::RuntimeClass<winapp::Core::IFrameworkView>,
+ public metro_driver::ImePopupObserver,
+ public metro_driver::InputSourceObserver,
+ public metro_driver::TextServiceDelegate {
+ public:
+ ChromeAppViewAsh();
+ ~ChromeAppViewAsh() override;
+
+ // IViewProvider overrides.
+ IFACEMETHOD(Initialize)(winapp::Core::ICoreApplicationView* view) override;
+ IFACEMETHOD(SetWindow)(winui::Core::ICoreWindow* window) override;
+ IFACEMETHOD(Load)(HSTRING entryPoint) override;
+ IFACEMETHOD(Run)() override;
+ IFACEMETHOD(Uninitialize)() override;
+
+ // Helper function to unsnap the chrome metro app if it is snapped.
+ // Returns S_OK on success.
+ static HRESULT Unsnap();
+
+ void OnActivateDesktop(const base::FilePath& file_path, bool ash_exit);
+ void OnOpenURLOnDesktop(const base::FilePath& shortcut,
+ const base::string16& url);
+ void OnSetCursor(HCURSOR cursor);
+ void OnDisplayFileOpenDialog(const base::string16& title,
+ const base::string16& filter,
+ const base::FilePath& default_path,
+ bool allow_multiple_files);
+ void OnDisplayFileSaveAsDialog(
+ const MetroViewerHostMsg_SaveAsDialogParams& params);
+ void OnDisplayFolderPicker(const base::string16& title);
+ void OnSetCursorPos(int x, int y);
+
+ // This function is invoked when the open file operation completes. The
+ // result of the operation is passed in along with the OpenFilePickerSession
+ // instance which is deleted after we read the required information from
+ // the OpenFilePickerSession class.
+ void OnOpenFileCompleted(OpenFilePickerSession* open_file_picker,
+ bool success);
+
+ // This function is invoked when the save file operation completes. The
+ // result of the operation is passed in along with the SaveFilePickerSession
+ // instance which is deleted after we read the required information from
+ // the SaveFilePickerSession class.
+ void OnSaveFileCompleted(SaveFilePickerSession* save_file_picker,
+ bool success);
+
+ // This function is invoked when the folder picker operation completes. The
+ // result of the operation is passed in along with the FolderPickerSession
+ // instance which is deleted after we read the required information from
+ // the FolderPickerSession class.
+ void OnFolderPickerCompleted(FolderPickerSession* folder_picker,
+ bool success);
+
+ void OnImeCancelComposition();
+ void OnImeUpdateTextInputClient(
+ const std::vector<int32_t>& input_scopes,
+ const std::vector<metro_viewer::CharacterBounds>& character_bounds);
+
+ void OnMetroExit(MetroTerminateMethod method);
+
+ HWND core_window_hwnd() const { return core_window_hwnd_; }
+
+
+ private:
+ class PointerInfoHandler;
+
+ // ImePopupObserver overrides.
+ void OnImePopupChanged(ImePopupObserver::EventType event) override;
+
+ // InputSourceObserver overrides.
+ void OnInputSourceChanged() override;
+
+ // TextServiceDelegate overrides.
+ void OnCompositionChanged(
+ const base::string16& text,
+ int32_t selection_start,
+ int32_t selection_end,
+ const std::vector<metro_viewer::UnderlineInfo>& underlines) override;
+ void OnTextCommitted(const base::string16& text) override;
+
+ // Convenience for sending a MetroViewerHostMsg_MouseButton with the specified
+ // parameters.
+ void SendMouseButton(int x,
+ int y,
+ int extra,
+ ui::EventType event_type,
+ uint32_t flags,
+ ui::EventFlags changed_button,
+ bool is_horizontal_wheel);
+
+ // Win8 only generates a mouse press for the initial button that goes down and
+ // a release when the last button is released. Any intermediary presses (or
+ // releases) do not result in a new press/release event. Instead a move is
+ // generated with the winui::Input::PointerUpdateKind identifying what
+ // changed. This function generates the necessary intermediary events (as
+ // necessary).
+ void GenerateMouseEventFromMoveIfNecessary(const PointerInfoHandler& pointer);
+
+ HRESULT OnActivate(winapp::Core::ICoreApplicationView* view,
+ winapp::Activation::IActivatedEventArgs* args);
+
+ HRESULT OnPointerMoved(winui::Core::ICoreWindow* sender,
+ winui::Core::IPointerEventArgs* args);
+
+ HRESULT OnPointerPressed(winui::Core::ICoreWindow* sender,
+ winui::Core::IPointerEventArgs* args);
+
+ HRESULT OnPointerReleased(winui::Core::ICoreWindow* sender,
+ winui::Core::IPointerEventArgs* args);
+
+ HRESULT OnWheel(winui::Core::ICoreWindow* sender,
+ winui::Core::IPointerEventArgs* args);
+
+ HRESULT OnKeyDown(winui::Core::ICoreWindow* sender,
+ winui::Core::IKeyEventArgs* args);
+
+ HRESULT OnKeyUp(winui::Core::ICoreWindow* sender,
+ winui::Core::IKeyEventArgs* args);
+
+ // Invoked for system keys like Alt, etc.
+ HRESULT OnAcceleratorKeyDown(winui::Core::ICoreDispatcher* sender,
+ winui::Core::IAcceleratorKeyEventArgs* args);
+
+ HRESULT OnCharacterReceived(winui::Core::ICoreWindow* sender,
+ winui::Core::ICharacterReceivedEventArgs* args);
+
+ HRESULT OnWindowActivated(winui::Core::ICoreWindow* sender,
+ winui::Core::IWindowActivatedEventArgs* args);
+
+ // Helper to handle search requests received via the search charm in ASH.
+ HRESULT HandleSearchRequest(winapp::Activation::IActivatedEventArgs* args);
+ // Helper to handle http/https url requests in ASH.
+ HRESULT HandleProtocolRequest(winapp::Activation::IActivatedEventArgs* args);
+
+ HRESULT OnEdgeGestureCompleted(winui::Input::IEdgeGesture* gesture,
+ winui::Input::IEdgeGestureEventArgs* args);
+
+ // Tasks posted to the UI thread to initiate the search/url navigation
+ // requests.
+ void OnSearchRequest(const base::string16& search_string);
+ void OnNavigateToUrl(const base::string16& url);
+
+ HRESULT OnSizeChanged(winui::Core::ICoreWindow* sender,
+ winui::Core::IWindowSizeChangedEventArgs* args);
+
+ // This function checks if the Chrome browser channel is initialized. If yes
+ // then it goes ahead and starts up the viewer in Chrome OS mode. If not it
+ // posts a delayed task and checks again. It does this for a duration of 10
+ // seconds and then bails.
+ void StartChromeOSMode();
+
+ mswr::ComPtr<winui::Core::ICoreWindow> window_;
+ mswr::ComPtr<winapp::Core::ICoreApplicationView> view_;
+ EventRegistrationToken activated_token_;
+ EventRegistrationToken pointermoved_token_;
+ EventRegistrationToken pointerpressed_token_;
+ EventRegistrationToken pointerreleased_token_;
+ EventRegistrationToken wheel_token_;
+ EventRegistrationToken keydown_token_;
+ EventRegistrationToken keyup_token_;
+ EventRegistrationToken character_received_token_;
+ EventRegistrationToken accel_keydown_token_;
+ EventRegistrationToken accel_keyup_token_;
+ EventRegistrationToken window_activated_token_;
+ EventRegistrationToken sizechange_token_;
+ EventRegistrationToken edgeevent_token_;
+
+ // Keep state about which button is currently down, if any, as PointerMoved
+ // events do not contain that state, but Ash's MouseEvents need it. Value is
+ // as a bitmask of ui::EventFlags.
+ uint32_t mouse_down_flags_;
+
+ // Set the D3D swap chain and nothing else.
+ metro_driver::Direct3DHelper direct3d_helper_;
+
+ // The IPC channel IO thread.
+ scoped_ptr<base::Thread> io_thread_;
+
+ // The channel to Chrome, in particular to the MetroViewerProcessHost.
+ scoped_ptr<IPC::ChannelProxy> ui_channel_;
+
+ // The actual window behind the view surface.
+ HWND core_window_hwnd_;
+
+ // UI message loop to allow message passing into this thread.
+ base::MessageLoopForUI ui_loop_;
+
+ // For IME support.
+ scoped_ptr<metro_driver::InputSource> input_source_;
+ scoped_ptr<metro_driver::TextService> text_service_;
+
+ // The metro device scale factor as reported by the winrt interfaces.
+ float metro_dpi_scale_;
+ // The win32 dpi scale which is queried via GetDeviceCaps. Please refer to
+ // ui/gfx/win/dpi.cc for more information.
+ float win32_dpi_scale_;
+
+ // The cursor set by the chroem browser process.
+ HCURSOR last_cursor_;
+
+ // Pointer to the channel listener for the channel between the viewer and
+ // the browser.
+ IPC::Listener* channel_listener_;
+};
+
+#endif // WIN8_METRO_DRIVER_CHROME_APP_VIEW_ASH_H_
diff --git a/win8/metro_driver/file_picker.cc b/win8/metro_driver/file_picker.cc
new file mode 100644
index 0000000..708b338
--- /dev/null
+++ b/win8/metro_driver/file_picker.cc
@@ -0,0 +1,622 @@
+// Copyright (c) 2012 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.
+
+#include "stdafx.h"
+#include "win8/metro_driver/file_picker.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <windows.storage.pickers.h>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_util.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/win/metro.h"
+#include "base/win/scoped_comptr.h"
+#include "win8/metro_driver/chrome_app_view.h"
+#include "win8/metro_driver/winrt_utils.h"
+
+namespace {
+
+namespace winstorage = ABI::Windows::Storage;
+typedef winfoundtn::Collections::IVector<HSTRING> StringVectorItf;
+
+// TODO(siggi): Complete this implementation and move it to a common place.
+class StringVectorImpl : public mswr::RuntimeClass<StringVectorItf> {
+ public:
+ ~StringVectorImpl() {
+ std::for_each(strings_.begin(), strings_.end(), ::WindowsDeleteString);
+ }
+
+ HRESULT RuntimeClassInitialize(const std::vector<base::string16>& list) {
+ for (size_t i = 0; i < list.size(); ++i)
+ strings_.push_back(MakeHString(list[i]));
+
+ return S_OK;
+ }
+
+ // IVector<HSTRING> implementation.
+ STDMETHOD(GetAt)(unsigned index, HSTRING* item) {
+ if (index >= strings_.size())
+ return E_INVALIDARG;
+
+ return ::WindowsDuplicateString(strings_[index], item);
+ }
+ STDMETHOD(get_Size)(unsigned *size) {
+ if (strings_.size() > UINT_MAX)
+ return E_UNEXPECTED;
+ *size = static_cast<unsigned>(strings_.size());
+ return S_OK;
+ }
+ STDMETHOD(GetView)(winfoundtn::Collections::IVectorView<HSTRING> **view) {
+ return E_NOTIMPL;
+ }
+ STDMETHOD(IndexOf)(HSTRING value, unsigned *index, boolean *found) {
+ return E_NOTIMPL;
+ }
+
+ // write methods
+ STDMETHOD(SetAt)(unsigned index, HSTRING item) {
+ return E_NOTIMPL;
+ }
+ STDMETHOD(InsertAt)(unsigned index, HSTRING item) {
+ return E_NOTIMPL;
+ }
+ STDMETHOD(RemoveAt)(unsigned index) {
+ return E_NOTIMPL;
+ }
+ STDMETHOD(Append)(HSTRING item) {
+ return E_NOTIMPL;
+ }
+ STDMETHOD(RemoveAtEnd)() {
+ return E_NOTIMPL;
+ }
+ STDMETHOD(Clear)() {
+ return E_NOTIMPL;
+ }
+
+ private:
+ std::vector<HSTRING> strings_;
+};
+
+class FilePickerSessionBase {
+ public:
+ // Creates a file picker for open_file_name.
+ explicit FilePickerSessionBase(OPENFILENAME* open_file_name);
+
+ // Runs the picker, returns true on success.
+ bool Run();
+
+ protected:
+ // Creates, configures and starts a file picker.
+ // If the HRESULT returned is a failure code the file picker has not started,
+ // so no callbacks should be expected.
+ virtual HRESULT StartFilePicker() = 0;
+
+ // The parameters to our picker.
+ OPENFILENAME* open_file_name_;
+ // The event Run waits on.
+ base::WaitableEvent event_;
+ // True iff a file picker has successfully finished.
+ bool success_;
+
+ private:
+ // Initiate a file picker, must be called on the metro dispatcher's thread.
+ void DoFilePicker();
+
+ DISALLOW_COPY_AND_ASSIGN(FilePickerSessionBase);
+};
+
+class OpenFilePickerSession : public FilePickerSessionBase {
+ public:
+ explicit OpenFilePickerSession(OPENFILENAME* open_file_name);
+
+ private:
+ HRESULT StartFilePicker() override;
+
+ typedef winfoundtn::IAsyncOperation<winstorage::StorageFile*>
+ SingleFileAsyncOp;
+ typedef winfoundtn::Collections::IVectorView<
+ winstorage::StorageFile*> StorageFileVectorCollection;
+ typedef winfoundtn::IAsyncOperation<StorageFileVectorCollection*>
+ MultiFileAsyncOp;
+
+ // Called asynchronously when a single file picker is done.
+ HRESULT SinglePickerDone(SingleFileAsyncOp* async, AsyncStatus status);
+
+ // Called asynchronously when a multi file picker is done.
+ HRESULT MultiPickerDone(MultiFileAsyncOp* async, AsyncStatus status);
+
+ // Composes a multi-file result string suitable for returning to a
+ // from a storage file collection.
+ static HRESULT ComposeMultiFileResult(StorageFileVectorCollection* files,
+ base::string16* result);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(OpenFilePickerSession);
+};
+
+class SaveFilePickerSession : public FilePickerSessionBase {
+ public:
+ explicit SaveFilePickerSession(OPENFILENAME* open_file_name);
+
+ private:
+ HRESULT StartFilePicker() override;
+
+ typedef winfoundtn::IAsyncOperation<winstorage::StorageFile*>
+ SaveFileAsyncOp;
+
+ // Called asynchronously when the save file picker is done.
+ HRESULT FilePickerDone(SaveFileAsyncOp* async, AsyncStatus status);
+};
+
+FilePickerSessionBase::FilePickerSessionBase(OPENFILENAME* open_file_name)
+ : open_file_name_(open_file_name),
+ event_(true, false),
+ success_(false) {
+}
+
+bool FilePickerSessionBase::Run() {
+ DCHECK(globals.appview_msg_loop != NULL);
+
+ // Post the picker request over to the metro thread.
+ bool posted = globals.appview_msg_loop->PostTask(FROM_HERE,
+ base::Bind(&FilePickerSessionBase::DoFilePicker, base::Unretained(this)));
+ if (!posted)
+ return false;
+
+ // Wait for the file picker to complete.
+ event_.Wait();
+
+ return success_;
+}
+
+void FilePickerSessionBase::DoFilePicker() {
+ // The file picker will fail if spawned from a snapped application,
+ // so let's attempt to unsnap first if we're in that state.
+ HRESULT hr = ChromeAppView::Unsnap();
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to unsnap for file picker, error 0x" << hr;
+ }
+
+ if (SUCCEEDED(hr))
+ hr = StartFilePicker();
+
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to start file picker, error 0x"
+ << std::hex << hr;
+
+ event_.Signal();
+ }
+}
+
+OpenFilePickerSession::OpenFilePickerSession(OPENFILENAME* open_file_name)
+ : FilePickerSessionBase(open_file_name) {
+}
+
+HRESULT OpenFilePickerSession::SinglePickerDone(SingleFileAsyncOp* async,
+ AsyncStatus status) {
+ if (status == Completed) {
+ mswr::ComPtr<winstorage::IStorageFile> file;
+ HRESULT hr = async->GetResults(file.GetAddressOf());
+
+ if (file) {
+ mswr::ComPtr<winstorage::IStorageItem> storage_item;
+ if (SUCCEEDED(hr))
+ hr = file.As(&storage_item);
+
+ mswrw::HString file_path;
+ if (SUCCEEDED(hr))
+ hr = storage_item->get_Path(file_path.GetAddressOf());
+
+ if (SUCCEEDED(hr)) {
+ UINT32 path_len = 0;
+ const wchar_t* path_str =
+ ::WindowsGetStringRawBuffer(file_path.Get(), &path_len);
+
+ // If the selected file name is longer than the supplied buffer,
+ // we return false as per GetOpenFileName documentation.
+ if (path_len < open_file_name_->nMaxFile) {
+ base::wcslcpy(open_file_name_->lpstrFile,
+ path_str,
+ open_file_name_->nMaxFile);
+ success_ = true;
+ }
+ }
+ } else {
+ LOG(ERROR) << "NULL IStorageItem";
+ }
+ } else {
+ LOG(ERROR) << "Unexpected async status " << static_cast<int>(status);
+ }
+
+ event_.Signal();
+
+ return S_OK;
+}
+
+HRESULT OpenFilePickerSession::MultiPickerDone(MultiFileAsyncOp* async,
+ AsyncStatus status) {
+ if (status == Completed) {
+ mswr::ComPtr<StorageFileVectorCollection> files;
+ HRESULT hr = async->GetResults(files.GetAddressOf());
+
+ if (files) {
+ base::string16 result;
+ if (SUCCEEDED(hr))
+ hr = ComposeMultiFileResult(files.Get(), &result);
+
+ if (SUCCEEDED(hr)) {
+ if (result.size() + 1 < open_file_name_->nMaxFile) {
+ // Because the result has embedded nulls, we must memcpy.
+ memcpy(open_file_name_->lpstrFile,
+ result.c_str(),
+ (result.size() + 1) * sizeof(result[0]));
+ success_ = true;
+ }
+ }
+ } else {
+ LOG(ERROR) << "NULL StorageFileVectorCollection";
+ }
+ } else {
+ LOG(ERROR) << "Unexpected async status " << static_cast<int>(status);
+ }
+
+ event_.Signal();
+
+ return S_OK;
+}
+
+HRESULT OpenFilePickerSession::StartFilePicker() {
+ DCHECK(globals.appview_msg_loop->BelongsToCurrentThread());
+ DCHECK(open_file_name_ != NULL);
+
+ mswrw::HStringReference class_name(
+ RuntimeClass_Windows_Storage_Pickers_FileOpenPicker);
+
+ // Create the file picker.
+ mswr::ComPtr<winstorage::Pickers::IFileOpenPicker> picker;
+ HRESULT hr = ::Windows::Foundation::ActivateInstance(
+ class_name.Get(), picker.GetAddressOf());
+ CheckHR(hr);
+
+ // Set the file type filter
+ mswr::ComPtr<winfoundtn::Collections::IVector<HSTRING>> filter;
+ hr = picker->get_FileTypeFilter(filter.GetAddressOf());
+ if (FAILED(hr))
+ return hr;
+
+ if (open_file_name_->lpstrFilter == NULL) {
+ hr = filter->Append(mswrw::HStringReference(L"*").Get());
+ if (FAILED(hr))
+ return hr;
+ } else {
+ // The filter is a concatenation of zero terminated string pairs,
+ // where each pair is {description, extension}. The concatenation ends
+ // with a zero length string - e.g. a double zero terminator.
+ const wchar_t* walk = open_file_name_->lpstrFilter;
+ while (*walk != L'\0') {
+ // Walk past the description.
+ walk += wcslen(walk) + 1;
+
+ // We should have an extension, but bail on malformed filters.
+ if (*walk == L'\0')
+ break;
+
+ // There can be a single extension, or a list of semicolon-separated ones.
+ std::vector<base::string16> extensions_win32_style = base::SplitString(
+ walk, L";", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+
+ // Metro wants suffixes only, not patterns.
+ mswrw::HString extension;
+ for (size_t i = 0; i < extensions_win32_style.size(); ++i) {
+ if (extensions_win32_style[i] == L"*.*") {
+ // The wildcard filter is "*" for Metro. The string "*.*" produces
+ // an "invalid parameter" error.
+ hr = extension.Set(L"*");
+ } else {
+ // Metro wants suffixes only, not patterns.
+ base::string16 ext =
+ base::FilePath(extensions_win32_style[i]).Extension();
+ if ((ext.size() < 2) ||
+ (ext.find_first_of(L"*?") != base::string16::npos)) {
+ continue;
+ }
+ hr = extension.Set(ext.c_str());
+ }
+ if (SUCCEEDED(hr))
+ hr = filter->Append(extension.Get());
+ if (FAILED(hr))
+ return hr;
+ }
+
+ // Walk past the extension.
+ walk += wcslen(walk) + 1;
+ }
+ }
+
+ // Spin up a single or multi picker as appropriate.
+ if (open_file_name_->Flags & OFN_ALLOWMULTISELECT) {
+ mswr::ComPtr<MultiFileAsyncOp> completion;
+ hr = picker->PickMultipleFilesAsync(&completion);
+ if (FAILED(hr))
+ return hr;
+
+ // Create the callback method.
+ typedef winfoundtn::IAsyncOperationCompletedHandler<
+ StorageFileVectorCollection*> HandlerDoneType;
+ mswr::ComPtr<HandlerDoneType> handler(mswr::Callback<HandlerDoneType>(
+ this, &OpenFilePickerSession::MultiPickerDone));
+ DCHECK(handler.Get() != NULL);
+ hr = completion->put_Completed(handler.Get());
+
+ return hr;
+ } else {
+ mswr::ComPtr<SingleFileAsyncOp> completion;
+ hr = picker->PickSingleFileAsync(&completion);
+ if (FAILED(hr))
+ return hr;
+
+ // Create the callback method.
+ typedef winfoundtn::IAsyncOperationCompletedHandler<
+ winstorage::StorageFile*> HandlerDoneType;
+ mswr::ComPtr<HandlerDoneType> handler(mswr::Callback<HandlerDoneType>(
+ this, &OpenFilePickerSession::SinglePickerDone));
+ DCHECK(handler.Get() != NULL);
+ hr = completion->put_Completed(handler.Get());
+
+ return hr;
+ }
+}
+
+HRESULT OpenFilePickerSession::ComposeMultiFileResult(
+ StorageFileVectorCollection* files, base::string16* result) {
+ DCHECK(files != NULL);
+ DCHECK(result != NULL);
+
+ // Empty the output string.
+ result->clear();
+
+ unsigned int num_files = 0;
+ HRESULT hr = files->get_Size(&num_files);
+ if (FAILED(hr))
+ return hr;
+
+ // Make sure we return an error on an empty collection.
+ if (num_files == 0) {
+ DLOG(ERROR) << "Empty collection on input.";
+ return E_UNEXPECTED;
+ }
+
+ // This stores the base path that should be the parent of all the files.
+ base::FilePath base_path;
+
+ // Iterate through the collection and append the file paths to the result.
+ for (unsigned int i = 0; i < num_files; ++i) {
+ mswr::ComPtr<winstorage::IStorageFile> file;
+ hr = files->GetAt(i, file.GetAddressOf());
+ if (FAILED(hr))
+ return hr;
+
+ mswr::ComPtr<winstorage::IStorageItem> storage_item;
+ hr = file.As(&storage_item);
+ if (FAILED(hr))
+ return hr;
+
+ mswrw::HString file_path_str;
+ hr = storage_item->get_Path(file_path_str.GetAddressOf());
+ if (FAILED(hr))
+ return hr;
+
+ base::FilePath file_path(MakeStdWString(file_path_str.Get()));
+ if (base_path.empty()) {
+ DCHECK(result->empty());
+ base_path = file_path.DirName();
+
+ // Append the path, including the terminating zero.
+ // We do this only for the first file.
+ result->append(base_path.value().c_str(), base_path.value().size() + 1);
+ }
+ DCHECK(!result->empty());
+ DCHECK(!base_path.empty());
+ DCHECK(base_path == file_path.DirName());
+
+ // Append the base name, including the terminating zero.
+ base::FilePath base_name = file_path.BaseName();
+ result->append(base_name.value().c_str(), base_name.value().size() + 1);
+ }
+
+ DCHECK(!result->empty());
+
+ return S_OK;
+}
+
+SaveFilePickerSession::SaveFilePickerSession(OPENFILENAME* open_file_name)
+ : FilePickerSessionBase(open_file_name) {
+}
+
+HRESULT SaveFilePickerSession::StartFilePicker() {
+ DCHECK(globals.appview_msg_loop->BelongsToCurrentThread());
+ DCHECK(open_file_name_ != NULL);
+
+ mswrw::HStringReference class_name(
+ RuntimeClass_Windows_Storage_Pickers_FileSavePicker);
+
+ // Create the file picker.
+ mswr::ComPtr<winstorage::Pickers::IFileSavePicker> picker;
+ HRESULT hr = ::Windows::Foundation::ActivateInstance(
+ class_name.Get(), picker.GetAddressOf());
+ CheckHR(hr);
+
+ typedef winfoundtn::Collections::IMap<HSTRING, StringVectorItf*>
+ StringVectorMap;
+ mswr::ComPtr<StringVectorMap> choices;
+ hr = picker->get_FileTypeChoices(choices.GetAddressOf());
+ if (FAILED(hr))
+ return hr;
+
+ if (open_file_name_->lpstrFilter) {
+ // The filter is a concatenation of zero terminated string pairs,
+ // where each pair is {description, extension list}. The concatenation ends
+ // with a zero length string - e.g. a double zero terminator.
+ const wchar_t* walk = open_file_name_->lpstrFilter;
+ while (*walk != L'\0') {
+ mswrw::HString description;
+ hr = description.Set(walk);
+ if (FAILED(hr))
+ return hr;
+
+ // Walk past the description.
+ walk += wcslen(walk) + 1;
+
+ // We should have an extension, but bail on malformed filters.
+ if (*walk == L'\0')
+ break;
+
+ // There can be a single extension, or a list of semicolon-separated ones.
+ std::vector<base::string16> extensions_win32_style = base::SplitString(
+ walk, L";", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+
+ // Metro wants suffixes only, not patterns. Also, metro does not support
+ // the all files ("*") pattern in the save picker.
+ std::vector<base::string16> extensions;
+ for (size_t i = 0; i < extensions_win32_style.size(); ++i) {
+ base::string16 ext =
+ base::FilePath(extensions_win32_style[i]).Extension();
+ if ((ext.size() < 2) ||
+ (ext.find_first_of(L"*?") != base::string16::npos))
+ continue;
+ extensions.push_back(ext);
+ }
+
+ if (!extensions.empty()) {
+ // Convert to a Metro collection class.
+ mswr::ComPtr<StringVectorItf> list;
+ hr = mswr::MakeAndInitialize<StringVectorImpl>(
+ list.GetAddressOf(), extensions);
+ if (FAILED(hr))
+ return hr;
+
+ // Finally set the filter.
+ boolean replaced = FALSE;
+ hr = choices->Insert(description.Get(), list.Get(), &replaced);
+ if (FAILED(hr))
+ return hr;
+ DCHECK_EQ(FALSE, replaced);
+ }
+
+ // Walk past the extension(s).
+ walk += wcslen(walk) + 1;
+ }
+ }
+
+ // The save picker requires at least one choice. Callers are strongly advised
+ // to provide sensible choices. If none were given, fallback to .dat.
+ uint32_t num_choices = 0;
+ hr = choices->get_Size(&num_choices);
+ if (FAILED(hr))
+ return hr;
+
+ if (num_choices == 0) {
+ mswrw::HString description;
+ // TODO(grt): Get a properly translated string. This can't be done from
+ // within metro_driver. Consider preprocessing the filter list in Chrome
+ // land to ensure it has this entry if all others are patterns. In that
+ // case, this whole block of code can be removed.
+ hr = description.Set(L"Data File");
+ if (FAILED(hr))
+ return hr;
+
+ mswr::ComPtr<StringVectorItf> list;
+ hr = mswr::MakeAndInitialize<StringVectorImpl>(
+ list.GetAddressOf(), std::vector<base::string16>(1, L".dat"));
+ if (FAILED(hr))
+ return hr;
+
+ boolean replaced = FALSE;
+ hr = choices->Insert(description.Get(), list.Get(), &replaced);
+ if (FAILED(hr))
+ return hr;
+ DCHECK_EQ(FALSE, replaced);
+ }
+
+ if (open_file_name_->lpstrFile != NULL) {
+ hr = picker->put_SuggestedFileName(
+ mswrw::HStringReference(
+ const_cast<const wchar_t*>(open_file_name_->lpstrFile)).Get());
+ if (FAILED(hr))
+ return hr;
+ }
+
+ mswr::ComPtr<SaveFileAsyncOp> completion;
+ hr = picker->PickSaveFileAsync(&completion);
+ if (FAILED(hr))
+ return hr;
+
+ // Create the callback method.
+ typedef winfoundtn::IAsyncOperationCompletedHandler<
+ winstorage::StorageFile*> HandlerDoneType;
+ mswr::ComPtr<HandlerDoneType> handler(mswr::Callback<HandlerDoneType>(
+ this, &SaveFilePickerSession::FilePickerDone));
+ DCHECK(handler.Get() != NULL);
+ hr = completion->put_Completed(handler.Get());
+
+ return hr;
+}
+
+HRESULT SaveFilePickerSession::FilePickerDone(SaveFileAsyncOp* async,
+ AsyncStatus status) {
+ if (status == Completed) {
+ mswr::ComPtr<winstorage::IStorageFile> file;
+ HRESULT hr = async->GetResults(file.GetAddressOf());
+
+ if (file) {
+ mswr::ComPtr<winstorage::IStorageItem> storage_item;
+ if (SUCCEEDED(hr))
+ hr = file.As(&storage_item);
+
+ mswrw::HString file_path;
+ if (SUCCEEDED(hr))
+ hr = storage_item->get_Path(file_path.GetAddressOf());
+
+ if (SUCCEEDED(hr)) {
+ base::string16 path_str = MakeStdWString(file_path.Get());
+
+ // If the selected file name is longer than the supplied buffer,
+ // we return false as per GetOpenFileName documentation.
+ if (path_str.size() < open_file_name_->nMaxFile) {
+ base::wcslcpy(open_file_name_->lpstrFile,
+ path_str.c_str(),
+ open_file_name_->nMaxFile);
+ success_ = true;
+ }
+ }
+ } else {
+ LOG(ERROR) << "NULL IStorageItem";
+ }
+ } else {
+ LOG(ERROR) << "Unexpected async status " << static_cast<int>(status);
+ }
+
+ event_.Signal();
+
+ return S_OK;
+}
+
+} // namespace
+
+BOOL MetroGetOpenFileName(OPENFILENAME* open_file_name) {
+ OpenFilePickerSession session(open_file_name);
+
+ return session.Run();
+}
+
+BOOL MetroGetSaveFileName(OPENFILENAME* open_file_name) {
+ SaveFilePickerSession session(open_file_name);
+
+ return session.Run();
+}
diff --git a/win8/metro_driver/file_picker.h b/win8/metro_driver/file_picker.h
new file mode 100644
index 0000000..ef56cb3
--- /dev/null
+++ b/win8/metro_driver/file_picker.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2012 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 CHROME_BROWSER_UI_METRO_DRIVER_FILE_PICKER_H_
+#define CHROME_BROWSER_UI_METRO_DRIVER_FILE_PICKER_H_
+
+#include <commdlg.h>
+
+// This function behaves similarly to GetOpenFileName, except it uses a
+// Metro file picker to pick a single or multiple file names.
+extern "C" __declspec(dllexport)
+BOOL MetroGetOpenFileName(OPENFILENAME* open_file_name);
+
+extern "C" __declspec(dllexport)
+BOOL MetroGetSaveFileName(OPENFILENAME* open_file_name);
+
+#endif // CHROME_BROWSER_UI_METRO_DRIVER_FILE_PICKER_H_
+
diff --git a/win8/metro_driver/file_picker_ash.cc b/win8/metro_driver/file_picker_ash.cc
new file mode 100644
index 0000000..31e699d
--- /dev/null
+++ b/win8/metro_driver/file_picker_ash.cc
@@ -0,0 +1,619 @@
+// 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.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "stdafx.h"
+#include "win8/metro_driver/file_picker_ash.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/win/scoped_comptr.h"
+#include "ui/metro_viewer/metro_viewer_messages.h"
+#include "win8/metro_driver/chrome_app_view_ash.h"
+#include "win8/metro_driver/winrt_utils.h"
+
+namespace {
+
+typedef winfoundtn::Collections::IVector<HSTRING> StringVectorItf;
+
+// TODO(siggi): Complete this implementation and move it to a common place.
+class StringVectorImpl : public mswr::RuntimeClass<StringVectorItf> {
+ public:
+ ~StringVectorImpl() override {
+ std::for_each(strings_.begin(), strings_.end(), ::WindowsDeleteString);
+ }
+
+ HRESULT RuntimeClassInitialize(const std::vector<base::string16>& list) {
+ for (size_t i = 0; i < list.size(); ++i)
+ strings_.push_back(MakeHString(list[i]));
+
+ return S_OK;
+ }
+
+ // IVector<HSTRING> implementation.
+ STDMETHOD(GetAt)(unsigned index, HSTRING* item) override {
+ if (index >= strings_.size())
+ return E_INVALIDARG;
+
+ return ::WindowsDuplicateString(strings_[index], item);
+ }
+ STDMETHOD(get_Size)(unsigned* size) override {
+ if (strings_.size() > UINT_MAX)
+ return E_UNEXPECTED;
+ *size = static_cast<unsigned>(strings_.size());
+ return S_OK;
+ }
+ STDMETHOD(GetView)(
+ winfoundtn::Collections::IVectorView<HSTRING>** view) override {
+ return E_NOTIMPL;
+ }
+ STDMETHOD(IndexOf)(HSTRING value, unsigned* index, boolean* found) override {
+ return E_NOTIMPL;
+ }
+
+ // write methods
+ STDMETHOD(SetAt)(unsigned index, HSTRING item) override { return E_NOTIMPL; }
+ STDMETHOD(InsertAt)(unsigned index, HSTRING item) override {
+ return E_NOTIMPL;
+ }
+ STDMETHOD(RemoveAt)(unsigned index) override { return E_NOTIMPL; }
+ STDMETHOD(Append)(HSTRING item) override { return E_NOTIMPL; }
+ STDMETHOD(RemoveAtEnd)() override { return E_NOTIMPL; }
+ STDMETHOD(Clear)() override { return E_NOTIMPL; }
+
+ private:
+ std::vector<HSTRING> strings_;
+};
+
+} // namespace
+
+FilePickerSessionBase::~FilePickerSessionBase() {
+}
+
+bool FilePickerSessionBase::Run() {
+ if (!DoFilePicker())
+ return false;
+ return success_;
+}
+
+FilePickerSessionBase::FilePickerSessionBase(ChromeAppViewAsh* app_view,
+ const base::string16& title,
+ const base::string16& filter,
+ const base::FilePath& default_path)
+ : success_(false),
+ title_(title),
+ filter_(filter),
+ default_path_(default_path),
+ app_view_(app_view) {
+}
+
+bool FilePickerSessionBase::DoFilePicker() {
+ // The file picker will fail if spawned from a snapped application,
+ // so let's attempt to unsnap first if we're in that state.
+ HRESULT hr = ChromeAppViewAsh::Unsnap();
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to unsnap for file picker, error 0x" << hr;
+ return false;
+ }
+ hr = StartFilePicker();
+ if (FAILED(hr)) {
+ LOG(ERROR) << "Failed to start file picker, error 0x"
+ << std::hex << hr;
+ return false;
+ }
+ return true;
+}
+
+OpenFilePickerSession::OpenFilePickerSession(
+ ChromeAppViewAsh* app_view,
+ const base::string16& title,
+ const base::string16& filter,
+ const base::FilePath& default_path,
+ bool allow_multi_select)
+ : FilePickerSessionBase(app_view, title, filter, default_path),
+ allow_multi_select_(allow_multi_select) {
+}
+
+OpenFilePickerSession::~OpenFilePickerSession() {
+}
+
+HRESULT OpenFilePickerSession::SinglePickerDone(SingleFileAsyncOp* async,
+ AsyncStatus status) {
+ if (status == Completed) {
+ mswr::ComPtr<winstorage::IStorageFile> file;
+ HRESULT hr = async->GetResults(file.GetAddressOf());
+
+ if (file) {
+ mswr::ComPtr<winstorage::IStorageItem> storage_item;
+ if (SUCCEEDED(hr))
+ hr = file.As(&storage_item);
+
+ mswrw::HString file_path;
+ if (SUCCEEDED(hr))
+ hr = storage_item->get_Path(file_path.GetAddressOf());
+
+ if (SUCCEEDED(hr)) {
+ UINT32 path_len = 0;
+ const wchar_t* path_str =
+ ::WindowsGetStringRawBuffer(file_path.Get(), &path_len);
+
+ result_ = path_str;
+ success_ = true;
+ }
+ } else {
+ LOG(ERROR) << "NULL IStorageItem";
+ }
+ } else {
+ LOG(ERROR) << "Unexpected async status " << static_cast<int>(status);
+ }
+ app_view_->OnOpenFileCompleted(this, success_);
+ return S_OK;
+}
+
+HRESULT OpenFilePickerSession::MultiPickerDone(MultiFileAsyncOp* async,
+ AsyncStatus status) {
+ if (status == Completed) {
+ mswr::ComPtr<StorageFileVectorCollection> files;
+ HRESULT hr = async->GetResults(files.GetAddressOf());
+
+ if (files) {
+ base::string16 result;
+ if (SUCCEEDED(hr))
+ hr = ComposeMultiFileResult(files.Get(), &result);
+
+ if (SUCCEEDED(hr)) {
+ success_ = true;
+ // The code below has been copied from the
+ // SelectFileDialogImpl::RunOpenMultiFileDialog function in
+ // select_file_dialog_win.cc.
+ // TODO(ananta)
+ // Consolidate this into a common place.
+ const wchar_t* selection = result.c_str();
+ std::vector<base::FilePath> files;
+
+ while (*selection) { // Empty string indicates end of list.
+ files.push_back(base::FilePath(selection));
+ // Skip over filename and null-terminator.
+ selection += files.back().value().length() + 1;
+ }
+ if (files.empty()) {
+ success_ = false;
+ } else if (files.size() == 1) {
+ // When there is one file, it contains the path and filename.
+ filenames_ = files;
+ } else if (files.size() > 1) {
+ // Otherwise, the first string is the path, and the remainder are
+ // filenames.
+ std::vector<base::FilePath>::iterator path = files.begin();
+ for (std::vector<base::FilePath>::iterator file = path + 1;
+ file != files.end(); ++file) {
+ filenames_.push_back(path->Append(*file));
+ }
+ }
+ }
+ } else {
+ LOG(ERROR) << "NULL StorageFileVectorCollection";
+ }
+ } else {
+ LOG(ERROR) << "Unexpected async status " << static_cast<int>(status);
+ }
+ app_view_->OnOpenFileCompleted(this, success_);
+ return S_OK;
+}
+
+HRESULT OpenFilePickerSession::StartFilePicker() {
+ mswrw::HStringReference class_name(
+ RuntimeClass_Windows_Storage_Pickers_FileOpenPicker);
+
+ // Create the file picker.
+ mswr::ComPtr<winstorage::Pickers::IFileOpenPicker> picker;
+ HRESULT hr = ::Windows::Foundation::ActivateInstance(
+ class_name.Get(), picker.GetAddressOf());
+ CheckHR(hr);
+
+ // Set the file type filter
+ mswr::ComPtr<winfoundtn::Collections::IVector<HSTRING>> filter;
+ hr = picker->get_FileTypeFilter(filter.GetAddressOf());
+ if (FAILED(hr))
+ return hr;
+
+ if (filter_.empty()) {
+ hr = filter->Append(mswrw::HStringReference(L"*").Get());
+ if (FAILED(hr))
+ return hr;
+ } else {
+ // The filter is a concatenation of zero terminated string pairs,
+ // where each pair is {description, extension}. The concatenation ends
+ // with a zero length string - e.g. a double zero terminator.
+ const wchar_t* walk = filter_.c_str();
+ while (*walk != L'\0') {
+ // Walk past the description.
+ walk += wcslen(walk) + 1;
+
+ // We should have an extension, but bail on malformed filters.
+ if (*walk == L'\0')
+ break;
+
+ // There can be a single extension, or a list of semicolon-separated ones.
+ std::vector<base::string16> extensions_win32_style = base::SplitString(
+ walk, L";", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+
+ // Metro wants suffixes only, not patterns.
+ mswrw::HString extension;
+ for (size_t i = 0; i < extensions_win32_style.size(); ++i) {
+ if (extensions_win32_style[i] == L"*.*") {
+ // The wildcard filter is "*" for Metro. The string "*.*" produces
+ // an "invalid parameter" error.
+ hr = extension.Set(L"*");
+ } else {
+ // Metro wants suffixes only, not patterns.
+ base::string16 ext =
+ base::FilePath(extensions_win32_style[i]).Extension();
+ if ((ext.size() < 2) ||
+ (ext.find_first_of(L"*?") != base::string16::npos)) {
+ continue;
+ }
+ hr = extension.Set(ext.c_str());
+ }
+ if (SUCCEEDED(hr))
+ hr = filter->Append(extension.Get());
+ if (FAILED(hr))
+ return hr;
+ }
+
+ // Walk past the extension.
+ walk += wcslen(walk) + 1;
+ }
+ }
+
+ // Spin up a single or multi picker as appropriate.
+ if (allow_multi_select_) {
+ mswr::ComPtr<MultiFileAsyncOp> completion;
+ hr = picker->PickMultipleFilesAsync(&completion);
+ if (FAILED(hr))
+ return hr;
+
+ // Create the callback method.
+ typedef winfoundtn::IAsyncOperationCompletedHandler<
+ StorageFileVectorCollection*> HandlerDoneType;
+ mswr::ComPtr<HandlerDoneType> handler(mswr::Callback<HandlerDoneType>(
+ this, &OpenFilePickerSession::MultiPickerDone));
+ DCHECK(handler.Get() != NULL);
+ hr = completion->put_Completed(handler.Get());
+
+ return hr;
+ } else {
+ mswr::ComPtr<SingleFileAsyncOp> completion;
+ hr = picker->PickSingleFileAsync(&completion);
+ if (FAILED(hr))
+ return hr;
+
+ // Create the callback method.
+ typedef winfoundtn::IAsyncOperationCompletedHandler<
+ winstorage::StorageFile*> HandlerDoneType;
+ mswr::ComPtr<HandlerDoneType> handler(mswr::Callback<HandlerDoneType>(
+ this, &OpenFilePickerSession::SinglePickerDone));
+ DCHECK(handler.Get() != NULL);
+ hr = completion->put_Completed(handler.Get());
+
+ return hr;
+ }
+}
+
+HRESULT OpenFilePickerSession::ComposeMultiFileResult(
+ StorageFileVectorCollection* files, base::string16* result) {
+ DCHECK(files != NULL);
+ DCHECK(result != NULL);
+
+ // Empty the output string.
+ result->clear();
+
+ unsigned int num_files = 0;
+ HRESULT hr = files->get_Size(&num_files);
+ if (FAILED(hr))
+ return hr;
+
+ // Make sure we return an error on an empty collection.
+ if (num_files == 0) {
+ DLOG(ERROR) << "Empty collection on input.";
+ return E_UNEXPECTED;
+ }
+
+ // This stores the base path that should be the parent of all the files.
+ base::FilePath base_path;
+
+ // Iterate through the collection and append the file paths to the result.
+ for (unsigned int i = 0; i < num_files; ++i) {
+ mswr::ComPtr<winstorage::IStorageFile> file;
+ hr = files->GetAt(i, file.GetAddressOf());
+ if (FAILED(hr))
+ return hr;
+
+ mswr::ComPtr<winstorage::IStorageItem> storage_item;
+ hr = file.As(&storage_item);
+ if (FAILED(hr))
+ return hr;
+
+ mswrw::HString file_path_str;
+ hr = storage_item->get_Path(file_path_str.GetAddressOf());
+ if (FAILED(hr))
+ return hr;
+
+ base::FilePath file_path(MakeStdWString(file_path_str.Get()));
+ if (base_path.empty()) {
+ DCHECK(result->empty());
+ base_path = file_path.DirName();
+
+ // Append the path, including the terminating zero.
+ // We do this only for the first file.
+ result->append(base_path.value().c_str(), base_path.value().size() + 1);
+ }
+ DCHECK(!result->empty());
+ DCHECK(!base_path.empty());
+ DCHECK(base_path == file_path.DirName());
+
+ // Append the base name, including the terminating zero.
+ base::FilePath base_name = file_path.BaseName();
+ result->append(base_name.value().c_str(), base_name.value().size() + 1);
+ }
+
+ DCHECK(!result->empty());
+
+ return S_OK;
+}
+
+SaveFilePickerSession::SaveFilePickerSession(
+ ChromeAppViewAsh* app_view,
+ const MetroViewerHostMsg_SaveAsDialogParams& params)
+ : FilePickerSessionBase(app_view,
+ params.title,
+ params.filter,
+ params.suggested_name),
+ filter_index_(params.filter_index) {
+}
+
+int SaveFilePickerSession::filter_index() const {
+ // TODO(ananta)
+ // Add support for returning the correct filter index. This does not work in
+ // regular Chrome metro on trunk as well.
+ // BUG: https://code.google.com/p/chromium/issues/detail?id=172704
+ return filter_index_;
+}
+
+HRESULT SaveFilePickerSession::StartFilePicker() {
+ mswrw::HStringReference class_name(
+ RuntimeClass_Windows_Storage_Pickers_FileSavePicker);
+
+ // Create the file picker.
+ mswr::ComPtr<winstorage::Pickers::IFileSavePicker> picker;
+ HRESULT hr = ::Windows::Foundation::ActivateInstance(
+ class_name.Get(), picker.GetAddressOf());
+ CheckHR(hr);
+
+ typedef winfoundtn::Collections::IMap<HSTRING, StringVectorItf*>
+ StringVectorMap;
+ mswr::ComPtr<StringVectorMap> choices;
+ hr = picker->get_FileTypeChoices(choices.GetAddressOf());
+ if (FAILED(hr))
+ return hr;
+
+ if (!filter_.empty()) {
+ // The filter is a concatenation of zero terminated string pairs,
+ // where each pair is {description, extension list}. The concatenation ends
+ // with a zero length string - e.g. a double zero terminator.
+ const wchar_t* walk = filter_.c_str();
+ while (*walk != L'\0') {
+ mswrw::HString description;
+ hr = description.Set(walk);
+ if (FAILED(hr))
+ return hr;
+
+ // Walk past the description.
+ walk += wcslen(walk) + 1;
+
+ // We should have an extension, but bail on malformed filters.
+ if (*walk == L'\0')
+ break;
+
+ // There can be a single extension, or a list of semicolon-separated ones.
+ std::vector<base::string16> extensions_win32_style = base::SplitString(
+ walk, L";", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+
+ // Metro wants suffixes only, not patterns. Also, metro does not support
+ // the all files ("*") pattern in the save picker.
+ std::vector<base::string16> extensions;
+ for (size_t i = 0; i < extensions_win32_style.size(); ++i) {
+ base::string16 ext =
+ base::FilePath(extensions_win32_style[i]).Extension();
+ if ((ext.size() < 2) ||
+ (ext.find_first_of(L"*?") != base::string16::npos))
+ continue;
+ extensions.push_back(ext);
+ }
+
+ if (!extensions.empty()) {
+ // Convert to a Metro collection class.
+ mswr::ComPtr<StringVectorItf> list;
+ hr = mswr::MakeAndInitialize<StringVectorImpl>(
+ list.GetAddressOf(), extensions);
+ if (FAILED(hr))
+ return hr;
+
+ // Finally set the filter.
+ boolean replaced = FALSE;
+ hr = choices->Insert(description.Get(), list.Get(), &replaced);
+ if (FAILED(hr))
+ return hr;
+ DCHECK_EQ(FALSE, replaced);
+ }
+
+ // Walk past the extension(s).
+ walk += wcslen(walk) + 1;
+ }
+ }
+
+ // The save picker requires at least one choice. Callers are strongly advised
+ // to provide sensible choices. If none were given, fallback to .dat.
+ uint32_t num_choices = 0;
+ hr = choices->get_Size(&num_choices);
+ if (FAILED(hr))
+ return hr;
+
+ if (num_choices == 0) {
+ mswrw::HString description;
+ // TODO(grt): Get a properly translated string. This can't be done from
+ // within metro_driver. Consider preprocessing the filter list in Chrome
+ // land to ensure it has this entry if all others are patterns. In that
+ // case, this whole block of code can be removed.
+ hr = description.Set(L"Data File");
+ if (FAILED(hr))
+ return hr;
+
+ mswr::ComPtr<StringVectorItf> list;
+ hr = mswr::MakeAndInitialize<StringVectorImpl>(
+ list.GetAddressOf(), std::vector<base::string16>(1, L".dat"));
+ if (FAILED(hr))
+ return hr;
+
+ boolean replaced = FALSE;
+ hr = choices->Insert(description.Get(), list.Get(), &replaced);
+ if (FAILED(hr))
+ return hr;
+ DCHECK_EQ(FALSE, replaced);
+ }
+
+ if (!default_path_.empty()) {
+ base::string16 file_part = default_path_.BaseName().value();
+ // If the suggested_name is a root directory, then don't set it as the
+ // suggested name.
+ if (file_part.size() == 1 && file_part[0] == L'\\')
+ file_part.clear();
+ hr = picker->put_SuggestedFileName(
+ mswrw::HStringReference(file_part.c_str()).Get());
+ if (FAILED(hr))
+ return hr;
+ }
+
+ mswr::ComPtr<SaveFileAsyncOp> completion;
+ hr = picker->PickSaveFileAsync(&completion);
+ if (FAILED(hr))
+ return hr;
+
+ // Create the callback method.
+ typedef winfoundtn::IAsyncOperationCompletedHandler<
+ winstorage::StorageFile*> HandlerDoneType;
+ mswr::ComPtr<HandlerDoneType> handler(mswr::Callback<HandlerDoneType>(
+ this, &SaveFilePickerSession::FilePickerDone));
+ DCHECK(handler.Get() != NULL);
+ hr = completion->put_Completed(handler.Get());
+
+ return hr;
+}
+
+HRESULT SaveFilePickerSession::FilePickerDone(SaveFileAsyncOp* async,
+ AsyncStatus status) {
+ if (status == Completed) {
+ mswr::ComPtr<winstorage::IStorageFile> file;
+ HRESULT hr = async->GetResults(file.GetAddressOf());
+
+ if (file) {
+ mswr::ComPtr<winstorage::IStorageItem> storage_item;
+ if (SUCCEEDED(hr))
+ hr = file.As(&storage_item);
+
+ mswrw::HString file_path;
+ if (SUCCEEDED(hr))
+ hr = storage_item->get_Path(file_path.GetAddressOf());
+
+ if (SUCCEEDED(hr)) {
+ base::string16 path_str = MakeStdWString(file_path.Get());
+ result_ = path_str;
+ success_ = true;
+ }
+ } else {
+ LOG(ERROR) << "NULL IStorageItem";
+ }
+ } else {
+ LOG(ERROR) << "Unexpected async status " << static_cast<int>(status);
+ }
+ app_view_->OnSaveFileCompleted(this, success_);
+ return S_OK;
+}
+
+FolderPickerSession::FolderPickerSession(ChromeAppViewAsh* app_view,
+ const base::string16& title)
+ : FilePickerSessionBase(app_view, title, L"", base::FilePath()) {
+}
+
+HRESULT FolderPickerSession::StartFilePicker() {
+ mswrw::HStringReference class_name(
+ RuntimeClass_Windows_Storage_Pickers_FolderPicker);
+
+ // Create the folder picker.
+ mswr::ComPtr<winstorage::Pickers::IFolderPicker> picker;
+ HRESULT hr = ::Windows::Foundation::ActivateInstance(
+ class_name.Get(), picker.GetAddressOf());
+ CheckHR(hr);
+
+ // Set the file type filter
+ mswr::ComPtr<winfoundtn::Collections::IVector<HSTRING>> filter;
+ hr = picker->get_FileTypeFilter(filter.GetAddressOf());
+ if (FAILED(hr))
+ return hr;
+
+ hr = filter->Append(mswrw::HStringReference(L"*").Get());
+ if (FAILED(hr))
+ return hr;
+
+ mswr::ComPtr<FolderPickerAsyncOp> completion;
+ hr = picker->PickSingleFolderAsync(&completion);
+ if (FAILED(hr))
+ return hr;
+
+ // Create the callback method.
+ typedef winfoundtn::IAsyncOperationCompletedHandler<
+ winstorage::StorageFolder*> HandlerDoneType;
+ mswr::ComPtr<HandlerDoneType> handler(mswr::Callback<HandlerDoneType>(
+ this, &FolderPickerSession::FolderPickerDone));
+ DCHECK(handler.Get() != NULL);
+ hr = completion->put_Completed(handler.Get());
+ return hr;
+}
+
+HRESULT FolderPickerSession::FolderPickerDone(FolderPickerAsyncOp* async,
+ AsyncStatus status) {
+ if (status == Completed) {
+ mswr::ComPtr<winstorage::IStorageFolder> folder;
+ HRESULT hr = async->GetResults(folder.GetAddressOf());
+
+ if (folder) {
+ mswr::ComPtr<winstorage::IStorageItem> storage_item;
+ if (SUCCEEDED(hr))
+ hr = folder.As(&storage_item);
+
+ mswrw::HString file_path;
+ if (SUCCEEDED(hr))
+ hr = storage_item->get_Path(file_path.GetAddressOf());
+
+ if (SUCCEEDED(hr)) {
+ base::string16 path_str = MakeStdWString(file_path.Get());
+ result_ = path_str;
+ success_ = true;
+ }
+ } else {
+ LOG(ERROR) << "NULL IStorageItem";
+ }
+ } else {
+ LOG(ERROR) << "Unexpected async status " << static_cast<int>(status);
+ }
+ app_view_->OnFolderPickerCompleted(this, success_);
+ return S_OK;
+}
+
diff --git a/win8/metro_driver/file_picker_ash.h b/win8/metro_driver/file_picker_ash.h
new file mode 100644
index 0000000..5d41a83
--- /dev/null
+++ b/win8/metro_driver/file_picker_ash.h
@@ -0,0 +1,157 @@
+// 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 CHROME_BROWSER_UI_METRO_DRIVER_FILE_PICKER_ASH_H_
+#define CHROME_BROWSER_UI_METRO_DRIVER_FILE_PICKER_ASH_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/strings/string16.h"
+
+class ChromeAppViewAsh;
+struct MetroViewerHostMsg_SaveAsDialogParams;
+
+namespace base {
+class FilePath;
+}
+
+// Base class for the file pickers.
+class FilePickerSessionBase {
+ public:
+ virtual ~FilePickerSessionBase();
+
+ // Runs the picker, returns true on success.
+ bool Run();
+
+ const base::string16& result() const { return result_; }
+
+ bool success() const { return success_; }
+
+ protected:
+ // Creates a file picker for open_file_name.
+ FilePickerSessionBase(ChromeAppViewAsh* app_view,
+ const base::string16& title,
+ const base::string16& filter,
+ const base::FilePath& default_path);
+
+ // Creates, configures and starts a file picker.
+ // If the HRESULT returned is a failure code the file picker has not started,
+ // so no callbacks should be expected.
+ virtual HRESULT StartFilePicker() = 0;
+
+ // True iff a file picker has successfully finished.
+ bool success_;
+
+ // The title of the file picker.
+ base::string16 title_;
+
+ // The file type filter.
+ base::string16 filter_;
+
+ // The starting directory/file name.
+ base::FilePath default_path_;
+
+ // Pointer to the ChromeAppViewAsh instance. We notify the ChromeAppViewAsh
+ // instance when the file open/save operations complete.
+ ChromeAppViewAsh* app_view_;
+
+ base::string16 result_;
+
+ private:
+ // Initiate a file picker, must be called on the main metro thread.
+ // Returns true on success.
+ bool DoFilePicker();
+
+ DISALLOW_COPY_AND_ASSIGN(FilePickerSessionBase);
+};
+
+// Provides functionality to display the open file/multiple open file pickers
+// metro dialog.
+class OpenFilePickerSession : public FilePickerSessionBase {
+ public:
+ OpenFilePickerSession(ChromeAppViewAsh* app_view,
+ const base::string16& title,
+ const base::string16& filter,
+ const base::FilePath& default_path,
+ bool allow_multi_select);
+ ~OpenFilePickerSession() override;
+
+ const std::vector<base::FilePath>& filenames() const {
+ return filenames_;
+ }
+
+ bool allow_multi_select() const { return allow_multi_select_; }
+
+ private:
+ HRESULT StartFilePicker() override;
+
+ typedef winfoundtn::IAsyncOperation<winstorage::StorageFile*>
+ SingleFileAsyncOp;
+ typedef winfoundtn::Collections::IVectorView<
+ winstorage::StorageFile*> StorageFileVectorCollection;
+ typedef winfoundtn::IAsyncOperation<StorageFileVectorCollection*>
+ MultiFileAsyncOp;
+
+ // Called asynchronously when a single file picker is done.
+ HRESULT SinglePickerDone(SingleFileAsyncOp* async, AsyncStatus status);
+
+ // Called asynchronously when a multi file picker is done.
+ HRESULT MultiPickerDone(MultiFileAsyncOp* async, AsyncStatus status);
+
+ // Composes a multi-file result string suitable for returning to a
+ // from a storage file collection.
+ static HRESULT ComposeMultiFileResult(StorageFileVectorCollection* files,
+ base::string16* result);
+
+ // True if the multi file picker is to be displayed.
+ bool allow_multi_select_;
+ // If multi select is true then this member contains the list of filenames
+ // to be returned back.
+ std::vector<base::FilePath> filenames_;
+
+ DISALLOW_COPY_AND_ASSIGN(OpenFilePickerSession);
+};
+
+// Provides functionality to display the save file picker.
+class SaveFilePickerSession : public FilePickerSessionBase {
+ public:
+ SaveFilePickerSession(ChromeAppViewAsh* app_view,
+ const MetroViewerHostMsg_SaveAsDialogParams& params);
+
+ int filter_index() const;
+
+ private:
+ HRESULT StartFilePicker() override;
+
+ typedef winfoundtn::IAsyncOperation<winstorage::StorageFile*> SaveFileAsyncOp;
+
+ // Called asynchronously when the save file picker is done.
+ HRESULT FilePickerDone(SaveFileAsyncOp* async, AsyncStatus status);
+
+ int filter_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(SaveFilePickerSession);
+};
+
+// Provides functionality to display the folder picker.
+class FolderPickerSession : public FilePickerSessionBase {
+ public:
+ FolderPickerSession(ChromeAppViewAsh* app_view, const base::string16& title);
+
+ private:
+ HRESULT StartFilePicker() override;
+
+ typedef winfoundtn::IAsyncOperation<winstorage::StorageFolder*>
+ FolderPickerAsyncOp;
+
+ // Called asynchronously when the folder picker is done.
+ HRESULT FolderPickerDone(FolderPickerAsyncOp* async, AsyncStatus status);
+
+ DISALLOW_COPY_AND_ASSIGN(FolderPickerSession);
+};
+
+#endif // CHROME_BROWSER_UI_METRO_DRIVER_FILE_PICKER_ASH_H_
+
diff --git a/win8/metro_driver/metro_driver.cc b/win8/metro_driver/metro_driver.cc
new file mode 100644
index 0000000..650b238
--- /dev/null
+++ b/win8/metro_driver/metro_driver.cc
@@ -0,0 +1,135 @@
+// Copyright (c) 2012 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.
+
+#include "stdafx.h"
+#include "win8/metro_driver/metro_driver.h"
+
+#include <roerrorapi.h>
+#include <shobjidl.h>
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/logging_win.h"
+#include "base/win/scoped_comptr.h"
+#include "base/win/windows_version.h"
+#include "win8/metro_driver/winrt_utils.h"
+
+namespace {
+
+#if !defined(NDEBUG)
+LONG WINAPI ErrorReportingHandler(EXCEPTION_POINTERS* ex_info) {
+ // See roerrorapi.h for a description of the
+ // exception codes and parameters.
+ DWORD code = ex_info->ExceptionRecord->ExceptionCode;
+ ULONG_PTR* info = ex_info->ExceptionRecord->ExceptionInformation;
+ if (code == EXCEPTION_RO_ORIGINATEERROR) {
+ base::string16 msg(reinterpret_cast<wchar_t*>(info[2]), info[1]);
+ LOG(ERROR) << "VEH: Metro error 0x" << std::hex << info[0] << ": " << msg;
+ } else if (code == EXCEPTION_RO_TRANSFORMERROR) {
+ base::string16 msg(reinterpret_cast<wchar_t*>(info[3]), info[2]);
+ LOG(ERROR) << "VEH: Metro old error 0x" << std::hex << info[0]
+ << " new error 0x" << info[1] << ": " << msg;
+ }
+
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+#endif
+
+void SetMetroReportingFlags() {
+#if !defined(NDEBUG)
+ // Set the error reporting flags to always raise an exception,
+ // which is then processed by our vectored exception handling
+ // above to log the error message.
+ winfoundtn::Diagnostics::SetErrorReportingFlags(
+ winfoundtn::Diagnostics::UseSetErrorInfo |
+ winfoundtn::Diagnostics::ForceExceptions);
+#endif
+}
+
+// TODO(robertshield): This GUID is hard-coded in a bunch of places that
+// don't allow explicit includes. Find a single place for it to live.
+// {7FE69228-633E-4f06-80C1-527FEA23E3A7}
+const GUID kChromeTraceProviderName = {
+ 0x7fe69228, 0x633e, 0x4f06,
+ { 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7 } };
+
+} // namespace
+
+#if !defined(COMPONENT_BUILD)
+// Required for base initialization.
+// TODO(siggi): This should be handled better, as this way our at exit
+// registrations will run under the loader's lock. However,
+// once metro_driver is merged into Chrome.dll, this will go away anyhow.
+base::AtExitManager at_exit;
+#endif
+
+mswr::ComPtr<winapp::Core::ICoreApplication> InitWindows8() {
+ SetMetroReportingFlags();
+ HRESULT hr = ::Windows::Foundation::Initialize(RO_INIT_MULTITHREADED);
+ if (FAILED(hr))
+ CHECK(false);
+ mswr::ComPtr<winapp::Core::ICoreApplication> core_app;
+ hr = winrt_utils::CreateActivationFactory(
+ RuntimeClass_Windows_ApplicationModel_Core_CoreApplication,
+ core_app.GetAddressOf());
+ if (FAILED(hr))
+ CHECK(false);
+ return core_app;
+}
+
+mswr::ComPtr<winapp::Core::ICoreApplication> InitWindows7();
+
+extern "C" __declspec(dllexport)
+int InitMetro() {
+ // Metro mode or its emulation is not supported in Vista or XP.
+ if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ return 1;
+ // Initialize the command line.
+ base::CommandLine::Init(0, NULL);
+ // Initialize the logging system.
+ logging::LoggingSettings settings;
+ settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+ logging::InitLogging(settings);
+#if defined(NDEBUG)
+ logging::SetMinLogLevel(logging::LOG_ERROR);
+#else
+ logging::SetMinLogLevel(logging::LOG_VERBOSE);
+ HANDLE registration =
+ ::AddVectoredExceptionHandler(TRUE, ErrorReportingHandler);
+#endif
+ // Enable trace control and transport through event tracing for Windows.
+ logging::LogEventProvider::Initialize(kChromeTraceProviderName);
+ DVLOG(1) << "InitMetro";
+
+ // OS specific initialization.
+ mswr::ComPtr<winapp::Core::ICoreApplication> core_app;
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ core_app = InitWindows7();
+ else
+ core_app = InitWindows8();
+
+ auto view_factory = mswr::Make<ChromeAppViewFactory>(core_app.Get());
+ HRESULT hr = core_app->Run(view_factory.Get());
+ DVLOG(1) << "exiting InitMetro, hr=" << hr;
+
+#if !defined(NDEBUG)
+ ::RemoveVectoredExceptionHandler(registration);
+#endif
+ return hr;
+}
+
+// Activates the application known by |app_id|. Returns, among other things,
+// E_APPLICATION_NOT_REGISTERED if |app_id| identifies Chrome and Chrome is not
+// the default browser.
+extern "C" __declspec(dllexport)
+HRESULT ActivateApplication(const wchar_t* app_id) {
+ base::win::ScopedComPtr<IApplicationActivationManager> activator;
+ HRESULT hr = activator.CreateInstance(CLSID_ApplicationActivationManager);
+ if (SUCCEEDED(hr)) {
+ DWORD pid = 0;
+ hr = activator->ActivateApplication(app_id, L"", AO_NONE, &pid);
+ }
+ return hr;
+}
diff --git a/win8/metro_driver/metro_driver.gyp b/win8/metro_driver/metro_driver.gyp
index a6f9afa..caa5ab5 100644
--- a/win8/metro_driver/metro_driver.gyp
+++ b/win8/metro_driver/metro_driver.gyp
@@ -65,6 +65,9 @@
'sources': [
'display_properties.cc',
'display_properties.h',
+ 'metro_driver.cc',
+ 'metro_driver.h',
+ 'metro_driver_win7.cc',
'stdafx.h',
'winrt_utils.cc',
'winrt_utils.h',
@@ -72,9 +75,16 @@
],
'conditions': [
['use_aura==1', {
+ 'dependencies': [
+ '../win8.gyp:metro_viewer_constants',
+ ],
'sources': [
+ 'chrome_app_view_ash.cc',
+ 'chrome_app_view_ash.h',
'direct3d_helper.cc',
'direct3d_helper.h',
+ 'file_picker_ash.cc',
+ 'file_picker_ash.h',
],
'includes': [
'ime/ime.gypi',
@@ -87,6 +97,8 @@
'chrome_url_launch_handler.h',
'devices_handler.cc',
'devices_handler.h',
+ 'file_picker.cc',
+ 'file_picker.h',
'metro_dialog_box.cc',
'metro_dialog_box.h',
'print_document_source.cc',
diff --git a/win8/metro_driver/metro_driver.h b/win8/metro_driver/metro_driver.h
new file mode 100644
index 0000000..da8f1c6
--- /dev/null
+++ b/win8/metro_driver/metro_driver.h
@@ -0,0 +1,17 @@
+// Copyright (c) 2012 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_METRO_DRIVER_METRO_DRIVER_H_
+#define WIN8_METRO_DRIVER_METRO_DRIVER_H_
+
+#include "stdafx.h"
+
+class ChromeAppViewFactory
+ : public mswr::RuntimeClass<winapp::Core::IFrameworkViewSource> {
+ public:
+ ChromeAppViewFactory(winapp::Core::ICoreApplication* icore_app);
+ IFACEMETHOD(CreateView)(winapp::Core::IFrameworkView** view) override;
+};
+
+#endif // WIN8_METRO_DRIVER_METRO_DRIVER_H_ \ No newline at end of file
diff --git a/win8/metro_driver/metro_driver_win7.cc b/win8/metro_driver/metro_driver_win7.cc
new file mode 100644
index 0000000..62662f5
--- /dev/null
+++ b/win8/metro_driver/metro_driver_win7.cc
@@ -0,0 +1,1229 @@
+// Copyright (c) 2012 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.
+
+#include "stdafx.h"
+#include <corewindow.h>
+#include <shobjidl.h>
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "ui/gfx/geometry/safe_integer_conversions.h"
+#include "ui/gfx/win/msg_util.h"
+
+#pragma comment(lib, "shell32.lib")
+
+EXTERN_C IMAGE_DOS_HEADER __ImageBase;
+// Even though we only create a single window, we need to keep this
+// count because of the hidden window used by the UI message loop of
+// the metro viewer.
+int g_window_count = 0;
+
+const wchar_t kAshWin7AppId[] = L"Google.Chrome.AshWin7.1";
+const wchar_t kAshWin7CoreWindowHandler[] = L"CoreWindowHandler";
+extern float GetModernUIScale();
+LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam,
+ LPARAM lparam);
+
+HWND CreateMetroTopLevelWindow(const RECT& work_area) {
+ HINSTANCE hInst = reinterpret_cast<HINSTANCE>(&__ImageBase);
+ WNDCLASSEXW wcex;
+ wcex.cbSize = sizeof(wcex);
+ wcex.style = CS_HREDRAW | CS_VREDRAW;
+ wcex.lpfnWndProc = WndProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = hInst;
+ wcex.hIcon = LoadIcon(::GetModuleHandle(NULL), L"IDR_MAINFRAME");
+ wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wcex.hbrBackground = (HBRUSH)(COLOR_INACTIVECAPTION+1);
+ wcex.lpszMenuName = 0;
+ wcex.lpszClassName = L"Windows.UI.Core.CoreWindow";
+ wcex.hIconSm = LoadIcon(::GetModuleHandle(NULL), L"IDR_MAINFRAME");
+
+ HWND hwnd = ::CreateWindowExW(0,
+ MAKEINTATOM(::RegisterClassExW(&wcex)),
+ L"metro_win7",
+ WS_POPUP | WS_VISIBLE | WS_MINIMIZEBOX,
+ work_area.top, work_area.left,
+ work_area.right, work_area.bottom,
+ NULL, NULL, hInst, NULL);
+ return hwnd;
+}
+
+typedef winfoundtn::ITypedEventHandler<
+ winapp::Core::CoreApplicationView*,
+ winapp::Activation::IActivatedEventArgs*> ActivatedHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::WindowActivatedEventArgs*> WindowActivatedHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::AutomationProviderRequestedEventArgs*>
+ AutomationProviderHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::CharacterReceivedEventArgs*> CharEventHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::CoreWindowEventArgs*> CoreWindowEventHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::InputEnabledEventArgs*> InputEnabledEventHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::KeyEventArgs*> KeyEventHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::PointerEventArgs*> PointerEventHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::WindowSizeChangedEventArgs*> SizeChangedHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::TouchHitTestingEventArgs*> TouchHitTestHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreWindow*,
+ winui::Core::VisibilityChangedEventArgs*> VisibilityChangedHandler;
+
+typedef winfoundtn::ITypedEventHandler<
+ winui::Core::CoreDispatcher*,
+ winui::Core::AcceleratorKeyEventArgs*> AcceleratorKeyEventHandler;
+
+// This interface is implemented by classes which handle mouse and keyboard
+// input.
+class InputHandler {
+ public:
+ InputHandler() {}
+ virtual ~InputHandler() {}
+
+ virtual bool HandleKeyboardMessage(const MSG& msg) = 0;
+ virtual bool HandleMouseMessage(const MSG& msg) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InputHandler);
+};
+
+// This class implements the winrt interfaces corresponding to mouse input.
+class MouseEvent : public mswr::RuntimeClass<
+ winui::Core::IPointerEventArgs,
+ winui::Input::IPointerPoint,
+ winui::Input::IPointerPointProperties,
+ windevs::Input::IPointerDevice> {
+ public:
+ MouseEvent(const MSG& msg)
+ : msg_(msg) {
+ }
+
+ // IPointerEventArgs implementation.
+ HRESULT STDMETHODCALLTYPE
+ get_CurrentPoint(winui::Input::IPointerPoint** point) override {
+ return QueryInterface(winui::Input::IID_IPointerPoint,
+ reinterpret_cast<void**>(point));
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ get_KeyModifiers(winsys::VirtualKeyModifiers* modifiers) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetIntermediatePoints(
+ winfoundtn::Collections::IVector<winui::Input::PointerPoint*>** points)
+ override {
+ return E_NOTIMPL;
+ }
+
+ // IPointerPoint implementation.
+ HRESULT STDMETHODCALLTYPE
+ get_PointerDevice(windevs::Input::IPointerDevice** pointer_device) override {
+ return QueryInterface(windevs::Input::IID_IPointerDevice,
+ reinterpret_cast<void**>(pointer_device));
+ }
+
+ HRESULT STDMETHODCALLTYPE get_Position(winfoundtn::Point* position) override {
+ static float scale = GetModernUIScale();
+ // Scale down the points here as they are scaled up on the other side.
+ position->X = gfx::ToRoundedInt(CR_GET_X_LPARAM(msg_.lParam) / scale);
+ position->Y = gfx::ToRoundedInt(CR_GET_Y_LPARAM(msg_.lParam) / scale);
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_PointerId(uint32_t* pointer_id) override {
+ // TODO(ananta)
+ // Implement this properly.
+ *pointer_id = 1;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_Timestamp(uint64_t* timestamp) override {
+ *timestamp = msg_.time;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ get_Properties(winui::Input::IPointerPointProperties** properties) override {
+ return QueryInterface(winui::Input::IID_IPointerPointProperties,
+ reinterpret_cast<void**>(properties));
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ get_RawPosition(winfoundtn::Point* position) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_FrameId(uint32_t* frame_id) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_IsInContact(boolean* in_contact) override {
+ return E_NOTIMPL;
+ }
+
+ // IPointerPointProperties implementation.
+ HRESULT STDMETHODCALLTYPE
+ get_PointerUpdateKind(winui::Input::PointerUpdateKind* update_kind) override {
+ // TODO(ananta)
+ // There is no WM_POINTERUPDATE equivalent on Windows 7. Look into
+ // equivalents.
+ if (msg_.message == WM_LBUTTONDOWN) {
+ *update_kind = winui::Input::PointerUpdateKind_LeftButtonPressed;
+ } else if (msg_.message == WM_RBUTTONDOWN) {
+ *update_kind = winui::Input::PointerUpdateKind_RightButtonPressed;
+ } else if (msg_.message == WM_MBUTTONDOWN) {
+ *update_kind = winui::Input::PointerUpdateKind_MiddleButtonPressed;
+ } else if (msg_.message == WM_LBUTTONUP) {
+ *update_kind = winui::Input::PointerUpdateKind_LeftButtonReleased;
+ } else if (msg_.message == WM_RBUTTONUP) {
+ *update_kind = winui::Input::PointerUpdateKind_RightButtonReleased;
+ } else if (msg_.message == WM_MBUTTONUP) {
+ *update_kind = winui::Input::PointerUpdateKind_MiddleButtonReleased;
+ }
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ get_IsLeftButtonPressed(boolean* left_button_pressed) override {
+ *left_button_pressed = msg_.wParam & MK_LBUTTON ? true : false;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ get_IsRightButtonPressed(boolean* right_button_pressed) override {
+ *right_button_pressed = msg_.wParam & MK_RBUTTON ? true : false;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ get_IsMiddleButtonPressed(boolean* middle_button_pressed) override {
+ *middle_button_pressed = msg_.wParam & MK_MBUTTON ? true : false;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ get_IsHorizontalMouseWheel(boolean* is_horizontal_mouse_wheel) override {
+ *is_horizontal_mouse_wheel =
+ (msg_.message == WM_MOUSEHWHEEL) ? true : false;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_MouseWheelDelta(int* delta) override {
+ if (msg_.message == WM_MOUSEWHEEL || msg_.message == WM_MOUSEHWHEEL) {
+ *delta = GET_WHEEL_DELTA_WPARAM(msg_.wParam);
+ return S_OK;
+ } else {
+ return S_FALSE;
+ }
+ }
+
+ HRESULT STDMETHODCALLTYPE get_Pressure(float* pressure) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_IsInverted(boolean* inverted) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_IsEraser(boolean* is_eraser) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_Orientation(float* orientation) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_XTilt(float* x_tilt) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_YTilt(float* y_tilt) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_Twist(float* twist) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_ContactRect(winfoundtn::Rect* rect) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ get_ContactRectRaw(winfoundtn::Rect* rect) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_TouchConfidence(boolean* confidence) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_IsPrimary(boolean* is_primary) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_IsInRange(boolean* is_in_range) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_IsCanceled(boolean* is_canceled) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ get_IsBarrelButtonPressed(boolean* is_barrel_button_pressed) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ get_IsXButton1Pressed(boolean* is_xbutton1_pressed) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ get_IsXButton2Pressed(boolean* is_xbutton2_pressed) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE HasUsage(uint32_t usage_page,
+ uint32_t usage_id,
+ boolean* has_usage) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetUsageValue(uint32_t usage_page,
+ uint32_t usage_id,
+ int32_t* usage_value) override {
+ return E_NOTIMPL;
+ }
+
+ // IPointerDevice implementation.
+ HRESULT STDMETHODCALLTYPE get_PointerDeviceType(
+ windevs::Input::PointerDeviceType* device_type) override {
+ if (msg_.message == WM_TOUCH) {
+ *device_type = windevs::Input::PointerDeviceType_Touch;
+ } else {
+ *device_type = windevs::Input::PointerDeviceType_Mouse;
+ }
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_IsIntegrated(boolean* is_integrated) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_MaxContacts(uint32_t* contacts) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ get_PhysicalDeviceRect(winfoundtn::Rect* rect) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_ScreenRect(winfoundtn::Rect* rect) override {
+ return E_NOTIMPL;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_SupportedUsages(
+ winfoundtn::Collections::IVectorView<windevs::Input::PointerDeviceUsage>**
+ usages) override {
+ return E_NOTIMPL;
+ }
+
+ private:
+ MSG msg_;
+
+ DISALLOW_COPY_AND_ASSIGN(MouseEvent);
+};
+
+// This class implements the winrt interfaces needed to support keyboard
+// character and system character messages.
+class KeyEvent : public mswr::RuntimeClass<
+ winui::Core::IKeyEventArgs,
+ winui::Core::ICharacterReceivedEventArgs,
+ winui::Core::IAcceleratorKeyEventArgs> {
+ public:
+ KeyEvent(const MSG& msg)
+ : msg_(msg) {}
+
+ // IKeyEventArgs implementation.
+ HRESULT STDMETHODCALLTYPE
+ get_VirtualKey(winsys::VirtualKey* virtual_key) override {
+ *virtual_key = static_cast<winsys::VirtualKey>(msg_.wParam);
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ get_KeyStatus(winui::Core::CorePhysicalKeyStatus* key_status) override {
+ // As per msdn documentation for the keyboard messages.
+ key_status->RepeatCount = msg_.lParam & 0x0000FFFF;
+ key_status->ScanCode = (msg_.lParam >> 16) & 0x00FF;
+ key_status->IsExtendedKey = (msg_.lParam & (1 << 24));
+ key_status->IsMenuKeyDown = (msg_.lParam & (1 << 29));
+ key_status->WasKeyDown = (msg_.lParam & (1 << 30));
+ key_status->IsKeyReleased = (msg_.lParam & (1 << 31));
+ return S_OK;
+ }
+
+ // ICharacterReceivedEventArgs implementation.
+ HRESULT STDMETHODCALLTYPE get_KeyCode(uint32_t* key_code) override {
+ *key_code = msg_.wParam;
+ return S_OK;
+ }
+
+ // IAcceleratorKeyEventArgs implementation.
+ HRESULT STDMETHODCALLTYPE
+ get_EventType(winui::Core::CoreAcceleratorKeyEventType* event_type) override {
+ if (msg_.message == WM_SYSKEYDOWN) {
+ *event_type = winui::Core::CoreAcceleratorKeyEventType_SystemKeyDown;
+ } else if (msg_.message == WM_SYSKEYUP) {
+ *event_type = winui::Core::CoreAcceleratorKeyEventType_SystemKeyUp;
+ } else if (msg_.message == WM_SYSCHAR) {
+ *event_type = winui::Core::CoreAcceleratorKeyEventType_SystemCharacter;
+ }
+ return S_OK;
+ }
+
+ private:
+ MSG msg_;
+};
+
+// The following classes are the emulation of the WinRT system as exposed
+// to metro applications. There is one application (ICoreApplication) which
+// contains a series of Views (ICoreApplicationView) each one of them
+// containing a CoreWindow which represents a surface that can drawn to
+// and that receives events.
+//
+// Here is the general dependency hierachy in terms of interfaces:
+//
+// IFrameworkViewSource --> IFrameworkView
+// ^ |
+// | | metro app
+// ---------------------------------------------------------------------
+// | | winRT system
+// | v
+// ICoreApplication ICoreApplicationView
+// |
+// v
+// ICoreWindow -----> ICoreWindowInterop
+// | |
+// | |
+// v V
+// ICoreDispatcher <==> real HWND
+//
+class CoreDispatcherEmulation :
+ public mswr::RuntimeClass<
+ winui::Core::ICoreDispatcher,
+ winui::Core::ICoreAcceleratorKeys> {
+ public:
+ CoreDispatcherEmulation(InputHandler* input_handler)
+ : input_handler_(input_handler),
+ accelerator_key_event_handler_(NULL) {}
+
+ // ICoreDispatcher implementation:
+ HRESULT STDMETHODCALLTYPE get_HasThreadAccess(boolean* value) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ ProcessEvents(winui::Core::CoreProcessEventsOption options) override {
+ // We don't support the other message pump modes. So we basically enter a
+ // traditional message loop that we only exit a teardown.
+ if (options != winui::Core::CoreProcessEventsOption_ProcessUntilQuit)
+ return E_FAIL;
+
+ MSG msg = {0};
+ while((::GetMessage(&msg, NULL, 0, 0) != 0) && g_window_count > 0) {
+ ProcessInputMessage(msg);
+ ::TranslateMessage(&msg);
+ ::DispatchMessage(&msg);
+ }
+ // TODO(cpu): figure what to do with msg.WParam which we would normally
+ // return here.
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ RunAsync(winui::Core::CoreDispatcherPriority priority,
+ winui::Core::IDispatchedHandler* agileCallback,
+ ABI::Windows::Foundation::IAsyncAction** asyncAction) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ RunIdleAsync(winui::Core::IIdleDispatchedHandler* agileCallback,
+ winfoundtn::IAsyncAction** asyncAction) override {
+ return S_OK;
+ }
+
+ // ICoreAcceleratorKeys implementation:
+ HRESULT STDMETHODCALLTYPE
+ add_AcceleratorKeyActivated(AcceleratorKeyEventHandler* handler,
+ EventRegistrationToken* pCookie) override {
+ accelerator_key_event_handler_ = handler;
+ accelerator_key_event_handler_->AddRef();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ remove_AcceleratorKeyActivated(EventRegistrationToken cookie) override {
+ accelerator_key_event_handler_->Release();
+ accelerator_key_event_handler_ = NULL;
+ return S_OK;
+ }
+
+ private:
+ bool ProcessInputMessage(const MSG& msg) {
+ // Poor man's way of dispatching input events.
+ bool ret = false;
+ if (input_handler_) {
+ if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST)) {
+ if ((msg.message == WM_SYSKEYDOWN) || (msg.message == WM_SYSKEYUP) ||
+ msg.message == WM_SYSCHAR) {
+ ret = HandleSystemKeys(msg);
+ } else {
+ ret = input_handler_->HandleKeyboardMessage(msg);
+ }
+ } else if ((msg.message >= WM_MOUSEFIRST) &&
+ (msg.message <= WM_MOUSELAST)) {
+ ret = input_handler_->HandleMouseMessage(msg);
+ }
+ }
+ return ret;
+ }
+
+ bool HandleSystemKeys(const MSG& msg) {
+ mswr::ComPtr<winui::Core::IAcceleratorKeyEventArgs> event_args;
+ event_args = mswr::Make<KeyEvent>(msg);
+ accelerator_key_event_handler_->Invoke(this, event_args.Get());
+ return true;
+ }
+
+ InputHandler* input_handler_;
+ AcceleratorKeyEventHandler* accelerator_key_event_handler_;
+};
+
+class CoreWindowEmulation
+ : public mswr::RuntimeClass<
+ mswr::RuntimeClassFlags<mswr::WinRtClassicComMix>,
+ winui::Core::ICoreWindow, ICoreWindowInterop>,
+ public InputHandler {
+ public:
+ CoreWindowEmulation(winapp::Core::IFrameworkView* app_view)
+ : mouse_moved_handler_(NULL),
+ mouse_capture_lost_handler_(NULL),
+ mouse_pressed_handler_(NULL),
+ mouse_released_handler_(NULL),
+ mouse_entered_handler_(NULL),
+ mouse_exited_handler_(NULL),
+ mouse_wheel_changed_handler_(NULL),
+ key_down_handler_(NULL),
+ key_up_handler_(NULL),
+ character_received_handler_(NULL),
+ core_hwnd_(NULL),
+ app_view_(app_view),
+ window_activated_handler_(NULL) {
+ dispatcher_ = mswr::Make<CoreDispatcherEmulation>(this);
+
+ // Unless we select our own AppUserModelID the shell might confuse us
+ // with the app launcher one and we get the wrong taskbar button and icon.
+ ::SetCurrentProcessExplicitAppUserModelID(kAshWin7AppId);
+
+ RECT work_area = {0};
+ ::SystemParametersInfo(SPI_GETWORKAREA, 0, &work_area, 0);
+ if (::IsDebuggerPresent()) {
+ work_area.top = 0;
+ work_area.left = 0;
+ work_area.right = 1600;
+ work_area.bottom = 900;
+ }
+
+ core_hwnd_ = CreateMetroTopLevelWindow(work_area);
+ ::SetProp(core_hwnd_, kAshWin7CoreWindowHandler, this);
+ }
+
+ ~CoreWindowEmulation() override {
+ if (core_hwnd_) {
+ ::RemoveProp(core_hwnd_, kAshWin7CoreWindowHandler);
+ ::DestroyWindow(core_hwnd_);
+ }
+ }
+
+ // ICoreWindow implementation:
+ HRESULT STDMETHODCALLTYPE
+ get_AutomationHostProvider(IInspectable** value) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_Bounds(winfoundtn::Rect* value) override {
+ RECT rect;
+ if (!::GetClientRect(core_hwnd_, &rect))
+ return E_FAIL;
+ value->Width = rect.right;
+ value->Height = rect.bottom;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ get_CustomProperties(winfoundtn::Collections::IPropertySet** value) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ get_Dispatcher(winui::Core::ICoreDispatcher** value) override {
+ return dispatcher_.CopyTo(value);
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ get_FlowDirection(winui::Core::CoreWindowFlowDirection* value) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ put_FlowDirection(winui::Core::CoreWindowFlowDirection value) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_IsInputEnabled(boolean* value) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE put_IsInputEnabled(boolean value) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ get_PointerCursor(winui::Core::ICoreCursor** value) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ put_PointerCursor(winui::Core::ICoreCursor* value) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ get_PointerPosition(winfoundtn::Point* value) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_Visible(boolean* value) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE Activate(void) override {
+ // After we fire OnActivate on the View, Chrome calls us back here.
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE Close(void) override {
+ ::PostMessage(core_hwnd_, WM_CLOSE, 0, 0);
+ core_hwnd_ = NULL;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetAsyncKeyState(
+ ABI::Windows::System::VirtualKey virtualKey,
+ winui::Core::CoreVirtualKeyStates* KeyState) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE GetKeyState(
+ ABI::Windows::System::VirtualKey virtualKey,
+ winui::Core::CoreVirtualKeyStates* KeyState) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE ReleasePointerCapture(void) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE SetPointerCapture(void) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE add_Activated(
+ WindowActivatedHandler* handler,
+ EventRegistrationToken* pCookie) override {
+ window_activated_handler_ = handler;
+ handler->AddRef();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE remove_Activated(
+ EventRegistrationToken cookie) override {
+ window_activated_handler_->Release();
+ window_activated_handler_ = NULL;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE add_AutomationProviderRequested(
+ AutomationProviderHandler* handler,
+ EventRegistrationToken* cookie) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE remove_AutomationProviderRequested(
+ EventRegistrationToken cookie) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE add_CharacterReceived(
+ CharEventHandler* handler,
+ EventRegistrationToken* pCookie) override {
+ character_received_handler_ = handler;
+ character_received_handler_->AddRef();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE remove_CharacterReceived(
+ EventRegistrationToken cookie) override {
+ character_received_handler_->Release();
+ character_received_handler_ = NULL;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE add_Closed(
+ CoreWindowEventHandler* handler,
+ EventRegistrationToken* pCookie) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE remove_Closed(
+ EventRegistrationToken cookie) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE add_InputEnabled(
+ InputEnabledEventHandler* handler,
+ EventRegistrationToken* pCookie) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE remove_InputEnabled(
+ EventRegistrationToken cookie) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE add_KeyDown(
+ KeyEventHandler* handler,
+ EventRegistrationToken* pCookie) override {
+ key_down_handler_ = handler;
+ key_down_handler_->AddRef();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE remove_KeyDown(
+ EventRegistrationToken cookie) override {
+ key_down_handler_->Release();
+ key_down_handler_ = NULL;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE add_KeyUp(
+ KeyEventHandler* handler,
+ EventRegistrationToken* pCookie) override {
+ key_up_handler_ = handler;
+ key_up_handler_->AddRef();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE remove_KeyUp(
+ EventRegistrationToken cookie) override {
+ key_up_handler_->Release();
+ key_up_handler_ = NULL;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE add_PointerCaptureLost(
+ PointerEventHandler* handler,
+ EventRegistrationToken* cookie) override {
+ mouse_capture_lost_handler_ = handler;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE remove_PointerCaptureLost(
+ EventRegistrationToken cookie) override {
+ mouse_capture_lost_handler_ = NULL;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE add_PointerEntered(
+ PointerEventHandler* handler,
+ EventRegistrationToken* cookie) override {
+ mouse_entered_handler_ = handler;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE remove_PointerEntered(
+ EventRegistrationToken cookie) override {
+ mouse_entered_handler_ = NULL;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE add_PointerExited(
+ PointerEventHandler* handler,
+ EventRegistrationToken* cookie) override {
+ mouse_exited_handler_ = handler;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE remove_PointerExited(
+ EventRegistrationToken cookie) override {
+ mouse_exited_handler_ = NULL;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE add_PointerMoved(
+ PointerEventHandler* handler,
+ EventRegistrationToken* cookie) override {
+ mouse_moved_handler_ = handler;
+ mouse_moved_handler_->AddRef();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE remove_PointerMoved(
+ EventRegistrationToken cookie) override {
+ mouse_moved_handler_->Release();
+ mouse_moved_handler_ = NULL;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE add_PointerPressed(
+ PointerEventHandler* handler,
+ EventRegistrationToken* cookie) override {
+ mouse_pressed_handler_ = handler;
+ mouse_pressed_handler_->AddRef();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE remove_PointerPressed(
+ EventRegistrationToken cookie) override {
+ mouse_pressed_handler_->Release();
+ mouse_pressed_handler_ = NULL;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE add_PointerReleased(
+ PointerEventHandler* handler,
+ EventRegistrationToken* cookie) override {
+ mouse_released_handler_ = handler;
+ mouse_released_handler_->AddRef();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE remove_PointerReleased(
+ EventRegistrationToken cookie) override {
+ mouse_released_handler_->Release();
+ mouse_released_handler_ = NULL;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE add_TouchHitTesting(
+ TouchHitTestHandler* handler,
+ EventRegistrationToken* pCookie) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE remove_TouchHitTesting(
+ EventRegistrationToken cookie) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE add_PointerWheelChanged(
+ PointerEventHandler* handler,
+ EventRegistrationToken* cookie) override {
+ mouse_wheel_changed_handler_ = handler;
+ mouse_wheel_changed_handler_->AddRef();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE remove_PointerWheelChanged(
+ EventRegistrationToken cookie) override {
+ mouse_wheel_changed_handler_->Release();
+ mouse_wheel_changed_handler_ = NULL;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE add_SizeChanged(
+ SizeChangedHandler* handler,
+ EventRegistrationToken* pCookie) override {
+ // TODO(cpu): implement this.
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE remove_SizeChanged(
+ EventRegistrationToken cookie) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE add_VisibilityChanged(
+ VisibilityChangedHandler* handler,
+ EventRegistrationToken* pCookie) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE remove_VisibilityChanged(
+ EventRegistrationToken cookie) override {
+ return S_OK;
+ }
+
+ // ICoreWindowInterop implementation:
+ HRESULT STDMETHODCALLTYPE get_WindowHandle(HWND* hwnd) override {
+ if (!core_hwnd_)
+ return E_FAIL;
+ *hwnd = core_hwnd_;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE put_MessageHandled(boolean value) override {
+ return S_OK;
+ }
+
+ // InputHandler
+ bool HandleKeyboardMessage(const MSG& msg) override {
+ switch (msg.message) {
+ case WM_KEYDOWN:
+ case WM_KEYUP: {
+ mswr::ComPtr<winui::Core::IKeyEventArgs> event_args;
+ event_args = mswr::Make<KeyEvent>(msg);
+ KeyEventHandler* handler = NULL;
+ if (msg.message == WM_KEYDOWN) {
+ handler = key_down_handler_;
+ } else {
+ handler = key_up_handler_;
+ }
+ handler->Invoke(this, event_args.Get());
+ break;
+ }
+
+ case WM_CHAR:
+ case WM_DEADCHAR:
+ case WM_UNICHAR: {
+ mswr::ComPtr<winui::Core::ICharacterReceivedEventArgs> event_args;
+ event_args = mswr::Make<KeyEvent>(msg);
+ character_received_handler_->Invoke(this, event_args.Get());
+ break;
+ }
+
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ bool HandleMouseMessage(const MSG& msg) override {
+ PointerEventHandler* handler = NULL;
+ mswr::ComPtr<winui::Core::IPointerEventArgs> event_args;
+ event_args = mswr::Make<MouseEvent>(msg);
+ switch (msg.message) {
+ case WM_MOUSEMOVE: {
+ handler = mouse_moved_handler_;
+ break;
+ }
+ case WM_LBUTTONDOWN: {
+ case WM_RBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ handler = mouse_pressed_handler_;
+ break;
+ }
+
+ case WM_LBUTTONUP: {
+ case WM_RBUTTONUP:
+ case WM_MBUTTONUP:
+ handler = mouse_released_handler_;
+ break;
+ }
+
+ case WM_MOUSEWHEEL: {
+ case WM_MOUSEHWHEEL:
+ handler = mouse_wheel_changed_handler_;
+ break;
+ }
+
+ default:
+ return false;
+ }
+ DCHECK(handler);
+ handler->Invoke(this, event_args.Get());
+ return true;
+ }
+
+ void OnWindowActivated() {
+ if (window_activated_handler_)
+ window_activated_handler_->Invoke(this, NULL);
+ }
+
+ private:
+ PointerEventHandler* mouse_moved_handler_;
+ PointerEventHandler* mouse_capture_lost_handler_;
+ PointerEventHandler* mouse_pressed_handler_;
+ PointerEventHandler* mouse_released_handler_;
+ PointerEventHandler* mouse_entered_handler_;
+ PointerEventHandler* mouse_exited_handler_;
+ PointerEventHandler* mouse_wheel_changed_handler_;
+ KeyEventHandler* key_down_handler_;
+ KeyEventHandler* key_up_handler_;
+ CharEventHandler* character_received_handler_;
+ HWND core_hwnd_;
+ mswr::ComPtr<winui::Core::ICoreDispatcher> dispatcher_;
+ mswr::ComPtr<winapp::Core::IFrameworkView> app_view_;
+ WindowActivatedHandler* window_activated_handler_;
+};
+
+LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
+ WPARAM wparam, LPARAM lparam) {
+ PAINTSTRUCT ps;
+ HDC hdc;
+ switch (message) {
+ case WM_ACTIVATE: {
+ // HIWORD(wparam) is 1 if the window is minimized.
+ bool active = (LOWORD(wparam) != WA_INACTIVE) && !HIWORD(wparam);
+ if (active) {
+ CoreWindowEmulation* core_window_handler =
+ reinterpret_cast<CoreWindowEmulation*>(
+ ::GetProp(hwnd, kAshWin7CoreWindowHandler));
+ if (core_window_handler)
+ core_window_handler->OnWindowActivated();
+ }
+ return ::DefWindowProc(hwnd, message, wparam, lparam);
+ }
+ case WM_CREATE:
+ ++g_window_count;
+ break;
+ case WM_PAINT:
+ hdc = ::BeginPaint(hwnd, &ps);
+ ::EndPaint(hwnd, &ps);
+ break;
+ case WM_CLOSE:
+ ::DestroyWindow(hwnd);
+ break;
+ case WM_DESTROY:
+ --g_window_count;
+ if (!g_window_count)
+ ::PostQuitMessage(0);
+ break;
+ // Always allow Chrome to set the cursor.
+ case WM_SETCURSOR:
+ return 1;
+ default:
+ return ::DefWindowProc(hwnd, message, wparam, lparam);
+ }
+ return 0;
+}
+
+class ActivatedEvent
+ : public mswr::RuntimeClass<winapp::Activation::IActivatedEventArgs> {
+ public:
+ ActivatedEvent(winapp::Activation::ActivationKind activation_kind)
+ : activation_kind_(activation_kind) {
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ get_Kind(winapp::Activation::ActivationKind* value) override {
+ *value = activation_kind_;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_PreviousExecutionState(
+ winapp::Activation::ApplicationExecutionState* value) override {
+ *value = winapp::Activation::ApplicationExecutionState_ClosedByUser;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ get_SplashScreen(winapp::Activation::ISplashScreen** value) override {
+ return E_FAIL;
+ }
+
+ private:
+ winapp::Activation::ActivationKind activation_kind_;
+};
+
+class CoreApplicationViewEmulation
+ : public mswr::RuntimeClass<winapp::Core::ICoreApplicationView> {
+ public:
+ CoreApplicationViewEmulation(winapp::Core::IFrameworkView* app_view) {
+ core_window_ = mswr::Make<CoreWindowEmulation>(app_view);
+ }
+
+ HRESULT Activate() {
+ if (activated_handler_) {
+ auto ae = mswr::Make<ActivatedEvent>(
+ winapp::Activation::ActivationKind_File);
+ return activated_handler_->Invoke(this, ae.Get());
+ } else {
+ return S_OK;
+ }
+ }
+
+ HRESULT Close() {
+ return core_window_->Close();
+ }
+
+ // ICoreApplicationView implementation:
+ HRESULT STDMETHODCALLTYPE
+ get_CoreWindow(winui::Core::ICoreWindow** value) override {
+ if (!core_window_)
+ return E_FAIL;
+ return core_window_.CopyTo(value);
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ add_Activated(ActivatedHandler* handler,
+ EventRegistrationToken* token) override {
+ // The real component supports multiple handles but we don't yet.
+ if (activated_handler_)
+ return E_FAIL;
+ activated_handler_ = handler;
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ remove_Activated(EventRegistrationToken token) override {
+ // Chrome never unregisters handlers, so we don't care about it.
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE get_IsMain(boolean* value) override { return S_OK; }
+
+ HRESULT STDMETHODCALLTYPE get_IsHosted(boolean* value) override {
+ return S_OK;
+ }
+
+ private:
+ mswr::ComPtr<CoreWindowEmulation> core_window_;
+ mswr::ComPtr<ActivatedHandler> activated_handler_;
+};
+
+class CoreApplicationWin7Emulation
+ : public mswr::RuntimeClass<winapp::Core::ICoreApplication,
+ winapp::Core::ICoreApplicationExit> {
+ public:
+ // ICoreApplication implementation:
+
+ HRESULT STDMETHODCALLTYPE get_Id(HSTRING* value) override { return S_OK; }
+
+ HRESULT STDMETHODCALLTYPE add_Suspending(
+ winfoundtn::IEventHandler<winapp::SuspendingEventArgs*>* handler,
+ EventRegistrationToken* token) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ remove_Suspending(EventRegistrationToken token) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ add_Resuming(winfoundtn::IEventHandler<IInspectable*>* handler,
+ EventRegistrationToken* token) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ remove_Resuming(EventRegistrationToken token) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ get_Properties(winfoundtn::Collections::IPropertySet** value) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ GetCurrentView(winapp::Core::ICoreApplicationView** value) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ Run(winapp::Core::IFrameworkViewSource* viewSource) override {
+ HRESULT hr = viewSource->CreateView(app_view_.GetAddressOf());
+ if (FAILED(hr))
+ return hr;
+ view_emulation_ = mswr::Make<CoreApplicationViewEmulation>(
+ app_view_.Get());
+ hr = app_view_->Initialize(view_emulation_.Get());
+ if (FAILED(hr))
+ return hr;
+ mswr::ComPtr<winui::Core::ICoreWindow> core_window;
+ hr = view_emulation_->get_CoreWindow(core_window.GetAddressOf());
+ if (FAILED(hr))
+ return hr;
+ hr = app_view_->SetWindow(core_window.Get());
+ if (FAILED(hr))
+ return hr;
+ hr = app_view_->Load(NULL);
+ if (FAILED(hr))
+ return hr;
+ hr = view_emulation_->Activate();
+ if (FAILED(hr))
+ return hr;
+ return app_view_->Run();
+ }
+
+ HRESULT STDMETHODCALLTYPE RunWithActivationFactories(
+ winfoundtn::IGetActivationFactory* activationFactoryCallback) override {
+ return S_OK;
+ }
+
+ // ICoreApplicationExit implementation:
+
+ HRESULT STDMETHODCALLTYPE Exit(void) override {
+ return view_emulation_->Close();
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ add_Exiting(winfoundtn::IEventHandler<IInspectable*>* handler,
+ EventRegistrationToken* token) override {
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE
+ remove_Exiting(EventRegistrationToken token) override {
+ return S_OK;
+ }
+
+ private:
+ mswr::ComPtr<winapp::Core::IFrameworkView> app_view_;
+ mswr::ComPtr<CoreApplicationViewEmulation> view_emulation_;
+};
+
+
+mswr::ComPtr<winapp::Core::ICoreApplication> InitWindows7() {
+ HRESULT hr = ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+ if (FAILED(hr))
+ CHECK(false);
+ return mswr::Make<CoreApplicationWin7Emulation>();
+}
+
diff --git a/win8/viewer/metro_viewer_constants.cc b/win8/viewer/metro_viewer_constants.cc
new file mode 100644
index 0000000..fd54fe4
--- /dev/null
+++ b/win8/viewer/metro_viewer_constants.cc
@@ -0,0 +1,13 @@
+// Copyright 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.
+
+#include "win8/viewer/metro_viewer_constants.h"
+
+namespace win8 {
+
+const char kMetroViewerIPCChannelName[] = "viewer";
+
+const wchar_t kMetroViewerConnectVerb[] = L"connect";
+
+} // namespace win8
diff --git a/win8/viewer/metro_viewer_constants.h b/win8/viewer/metro_viewer_constants.h
new file mode 100644
index 0000000..560a450
--- /dev/null
+++ b/win8/viewer/metro_viewer_constants.h
@@ -0,0 +1,20 @@
+// Copyright 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_VIEWER_METRO_VIEWER_CONSTANTS_H_
+#define WIN8_VIEWER_METRO_VIEWER_CONSTANTS_H_
+
+namespace win8 {
+
+// The name of the IPC channel between the browser process and the metro viewer
+// process.
+extern const char kMetroViewerIPCChannelName[];
+
+// Tells the viewer process to simply connect back without needing to launch a
+// browser process itself.
+extern const wchar_t kMetroViewerConnectVerb[];
+
+} // namespace win8
+
+#endif // WIN8_VIEWER_METRO_VIEWER_CONSTANTS_H_
diff --git a/win8/viewer/metro_viewer_exports.h b/win8/viewer/metro_viewer_exports.h
new file mode 100644
index 0000000..61ff7d5
--- /dev/null
+++ b/win8/viewer/metro_viewer_exports.h
@@ -0,0 +1,29 @@
+// Copyright 2014 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 METRO_VIEWER_METRO_VIEWER_H_
+#define METRO_VIEWER_METRO_VIEWER_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(METRO_VIEWER_IMPLEMENTATION)
+#define METRO_VIEWER_EXPORT __declspec(dllexport)
+#else
+#define METRO_VIEWER_EXPORT __declspec(dllimport)
+#endif // defined(METRO_VIEWER_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(METRO_VIEWER_IMPLEMENTATION)
+#define METRO_VIEWER_EXPORT __attribute__((visibility("default")))
+#else
+#define METRO_VIEWER_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define METRO_VIEWER_EXPORT
+#endif
+
+#endif // METRO_VIEWER_METRO_VIEWER_H_
diff --git a/win8/viewer/metro_viewer_process_host.cc b/win8/viewer/metro_viewer_process_host.cc
new file mode 100644
index 0000000..a365241
--- /dev/null
+++ b/win8/viewer/metro_viewer_process_host.cc
@@ -0,0 +1,346 @@
+// 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.
+
+#include "win8/viewer/metro_viewer_process_host.h"
+
+#include <shlobj.h>
+#include <stdint.h>
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/path_service.h"
+#include "base/process/process.h"
+#include "base/strings/string16.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/time/time.h"
+#include "base/win/scoped_comptr.h"
+#include "base/win/windows_version.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_message.h"
+#include "ipc/ipc_message_macros.h"
+#include "ui/aura/remote_window_tree_host_win.h"
+#include "ui/metro_viewer/metro_viewer_messages.h"
+#include "win8/viewer/metro_viewer_constants.h"
+
+namespace {
+
+const int kViewerProcessConnectionTimeoutSecs = 60;
+
+} // namespace
+
+namespace win8 {
+
+// static
+MetroViewerProcessHost* MetroViewerProcessHost::instance_ = NULL;
+
+MetroViewerProcessHost::InternalMessageFilter::InternalMessageFilter(
+ MetroViewerProcessHost* owner)
+ : owner_(owner) {
+}
+
+void MetroViewerProcessHost::InternalMessageFilter::OnChannelConnected(
+ int32_t peer_pid) {
+ owner_->NotifyChannelConnected();
+}
+
+MetroViewerProcessHost::InternalMessageFilter::~InternalMessageFilter() {
+}
+
+MetroViewerProcessHost::MetroViewerProcessHost(
+ const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner) {
+ DCHECK(!instance_);
+ instance_ = this;
+
+ channel_ = IPC::ChannelProxy::Create(kMetroViewerIPCChannelName,
+ IPC::Channel::MODE_NAMED_SERVER,
+ this,
+ ipc_task_runner);
+}
+
+MetroViewerProcessHost::~MetroViewerProcessHost() {
+ if (!channel_) {
+ instance_ = NULL;
+ return;
+ }
+
+ base::ProcessId viewer_process_id = GetViewerProcessId();
+ channel_->Close();
+ if (message_filter_.get()) {
+ // Wait for the viewer process to go away.
+ if (viewer_process_id != base::kNullProcessId) {
+ base::Process viewer_process =
+ base::Process::OpenWithAccess(
+ viewer_process_id,
+ PROCESS_QUERY_INFORMATION | SYNCHRONIZE);
+ if (viewer_process.IsValid()) {
+ int exit_code;
+ viewer_process.WaitForExit(&exit_code);
+ }
+ }
+ channel_->RemoveFilter(message_filter_.get());
+ }
+ instance_ = NULL;
+}
+
+base::ProcessId MetroViewerProcessHost::GetViewerProcessId() {
+ if (channel_)
+ return channel_->GetPeerPID();
+ return base::kNullProcessId;
+}
+
+bool MetroViewerProcessHost::LaunchViewerAndWaitForConnection(
+ const base::string16& app_user_model_id) {
+ DCHECK_EQ(base::kNullProcessId, channel_->GetPeerPID());
+
+ channel_connected_event_.reset(new base::WaitableEvent(false, false));
+
+ message_filter_ = new InternalMessageFilter(this);
+ channel_->AddFilter(message_filter_.get());
+
+ if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
+ base::win::ScopedComPtr<IApplicationActivationManager> activator;
+ HRESULT hr = activator.CreateInstance(CLSID_ApplicationActivationManager);
+ if (SUCCEEDED(hr)) {
+ DWORD pid = 0;
+ // Use the "connect" verb to
+ hr = activator->ActivateApplication(
+ app_user_model_id.c_str(), kMetroViewerConnectVerb, AO_NONE, &pid);
+ }
+
+ LOG_IF(ERROR, FAILED(hr)) << "Tried and failed to launch Metro Chrome. "
+ << "hr=" << std::hex << hr;
+ } else {
+ // For Windows 7 we need to launch the viewer ourselves.
+ base::FilePath chrome_path;
+ if (!PathService::Get(base::DIR_EXE, &chrome_path))
+ return false;
+ // TODO(cpu): launch with "-ServerName:DefaultBrowserServer"
+ // note that the viewer might try to launch chrome again.
+ CHECK(false);
+ }
+
+ // Having launched the viewer process, now we wait for it to connect.
+ bool success =
+ channel_connected_event_->TimedWait(base::TimeDelta::FromSeconds(
+ kViewerProcessConnectionTimeoutSecs));
+ channel_connected_event_.reset();
+ return success;
+}
+
+bool MetroViewerProcessHost::Send(IPC::Message* msg) {
+ return channel_->Send(msg);
+}
+
+bool MetroViewerProcessHost::OnMessageReceived(
+ const IPC::Message& message) {
+ DCHECK(CalledOnValidThread());
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(MetroViewerProcessHost, message)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_FileSaveAsDone,
+ OnFileSaveAsDone)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_FileOpenDone,
+ OnFileOpenDone)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_MultiFileOpenDone,
+ OnMultiFileOpenDone)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_OpenURL, OnOpenURL)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SearchRequest, OnHandleSearchRequest)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SelectFolderDone,
+ OnSelectFolderDone)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SetTargetSurface, OnSetTargetSurface)
+ IPC_MESSAGE_HANDLER(MetroViewerHostMsg_WindowSizeChanged,
+ OnWindowSizeChanged)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled ? true :
+ aura::RemoteWindowTreeHostWin::Instance()->OnMessageReceived(message);
+}
+
+// static
+void MetroViewerProcessHost::HandleActivateDesktop(
+ const base::FilePath& path,
+ bool ash_exit) {
+ if (instance_) {
+ instance_->Send(
+ new MetroViewerHostMsg_ActivateDesktop(path, ash_exit));
+ }
+}
+
+// static
+void MetroViewerProcessHost::HandleMetroExit() {
+ if (instance_)
+ instance_->Send(new MetroViewerHostMsg_MetroExit());
+}
+
+// static
+void MetroViewerProcessHost::HandleOpenFile(
+ const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ const OpenFileCompletion& on_success,
+ const FileSelectionCanceled& on_failure) {
+ if (instance_) {
+ instance_->HandleOpenFileImpl(title, default_path, filter, on_success,
+ on_failure);
+ }
+}
+
+// static
+void MetroViewerProcessHost::HandleOpenMultipleFiles(
+ const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ const OpenMultipleFilesCompletion& on_success,
+ const FileSelectionCanceled& on_failure) {
+ if (instance_) {
+ instance_->HandleOpenMultipleFilesImpl(title, default_path, filter,
+ on_success, on_failure);
+ }
+}
+
+// static
+void MetroViewerProcessHost::HandleSaveFile(
+ const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ int filter_index,
+ const base::string16& default_extension,
+ const SaveFileCompletion& on_success,
+ const FileSelectionCanceled& on_failure) {
+ if (instance_) {
+ instance_->HandleSaveFileImpl(title, default_path, filter, filter_index,
+ default_extension, on_success, on_failure);
+ }
+}
+
+// static
+void MetroViewerProcessHost::HandleSelectFolder(
+ const base::string16& title,
+ const SelectFolderCompletion& on_success,
+ const FileSelectionCanceled& on_failure) {
+ if (instance_)
+ instance_->HandleSelectFolderImpl(title, on_success, on_failure);
+}
+
+void MetroViewerProcessHost::HandleOpenFileImpl(
+ const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ const OpenFileCompletion& on_success,
+ const FileSelectionCanceled& on_failure) {
+ // Can only have one of these operations in flight.
+ DCHECK(file_open_completion_callback_.is_null());
+ DCHECK(failure_callback_.is_null());
+
+ file_open_completion_callback_ = on_success;
+ failure_callback_ = on_failure;
+
+ Send(new MetroViewerHostMsg_DisplayFileOpen(title, filter, default_path,
+ false));
+}
+
+void MetroViewerProcessHost::HandleOpenMultipleFilesImpl(
+ const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ const OpenMultipleFilesCompletion& on_success,
+ const FileSelectionCanceled& on_failure) {
+ // Can only have one of these operations in flight.
+ DCHECK(multi_file_open_completion_callback_.is_null());
+ DCHECK(failure_callback_.is_null());
+ multi_file_open_completion_callback_ = on_success;
+ failure_callback_ = on_failure;
+
+ Send(new MetroViewerHostMsg_DisplayFileOpen(title, filter, default_path,
+ true));
+}
+
+void MetroViewerProcessHost::HandleSaveFileImpl(
+ const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ int filter_index,
+ const base::string16& default_extension,
+ const SaveFileCompletion& on_success,
+ const FileSelectionCanceled& on_failure) {
+ MetroViewerHostMsg_SaveAsDialogParams params;
+ params.title = title;
+ params.default_extension = default_extension;
+ params.filter = filter;
+ params.filter_index = filter_index;
+ params.suggested_name = default_path;
+
+ // Can only have one of these operations in flight.
+ DCHECK(file_saveas_completion_callback_.is_null());
+ DCHECK(failure_callback_.is_null());
+ file_saveas_completion_callback_ = on_success;
+ failure_callback_ = on_failure;
+
+ Send(new MetroViewerHostMsg_DisplayFileSaveAs(params));
+}
+
+void MetroViewerProcessHost::HandleSelectFolderImpl(
+ const base::string16& title,
+ const SelectFolderCompletion& on_success,
+ const FileSelectionCanceled& on_failure) {
+ // Can only have one of these operations in flight.
+ DCHECK(select_folder_completion_callback_.is_null());
+ DCHECK(failure_callback_.is_null());
+ select_folder_completion_callback_ = on_success;
+ failure_callback_ = on_failure;
+
+ Send(new MetroViewerHostMsg_DisplaySelectFolder(title));
+}
+
+void MetroViewerProcessHost::NotifyChannelConnected() {
+ if (channel_connected_event_)
+ channel_connected_event_->Signal();
+}
+
+void MetroViewerProcessHost::OnFileSaveAsDone(bool success,
+ const base::FilePath& filename,
+ int filter_index) {
+ if (success)
+ file_saveas_completion_callback_.Run(filename, filter_index, NULL);
+ else
+ failure_callback_.Run(NULL);
+ file_saveas_completion_callback_.Reset();
+ failure_callback_.Reset();
+}
+
+
+void MetroViewerProcessHost::OnFileOpenDone(bool success,
+ const base::FilePath& filename) {
+ if (success)
+ file_open_completion_callback_.Run(base::FilePath(filename), 0, NULL);
+ else
+ failure_callback_.Run(NULL);
+ file_open_completion_callback_.Reset();
+ failure_callback_.Reset();
+}
+
+void MetroViewerProcessHost::OnMultiFileOpenDone(
+ bool success,
+ const std::vector<base::FilePath>& files) {
+ if (success)
+ multi_file_open_completion_callback_.Run(files, NULL);
+ else
+ failure_callback_.Run(NULL);
+ multi_file_open_completion_callback_.Reset();
+ failure_callback_.Reset();
+}
+
+void MetroViewerProcessHost::OnSelectFolderDone(
+ bool success,
+ const base::FilePath& folder) {
+ if (success)
+ select_folder_completion_callback_.Run(base::FilePath(folder), 0, NULL);
+ else
+ failure_callback_.Run(NULL);
+ select_folder_completion_callback_.Reset();
+ failure_callback_.Reset();
+}
+
+} // namespace win8
diff --git a/win8/viewer/metro_viewer_process_host.h b/win8/viewer/metro_viewer_process_host.h
new file mode 100644
index 0000000..2b1a56f
--- /dev/null
+++ b/win8/viewer/metro_viewer_process_host.h
@@ -0,0 +1,221 @@
+// 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_VIEWER_METRO_VIEWER_PROCESS_HOST_H_
+#define WIN8_VIEWER_METRO_VIEWER_PROCESS_HOST_H_
+
+#include <stdint.h>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string16.h"
+#include "base/threading/non_thread_safe.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_listener.h"
+#include "ipc/ipc_sender.h"
+#include "ipc/message_filter.h"
+#include "ui/gfx/native_widget_types.h"
+#include "win8/viewer/metro_viewer_exports.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+class WaitableEvent;
+}
+
+namespace IPC {
+class ChannelProxy;
+class Message;
+}
+
+namespace win8 {
+
+// Abstract base class for various Metro viewer process host implementations.
+class METRO_VIEWER_EXPORT MetroViewerProcessHost : public IPC::Listener,
+ public IPC::Sender,
+ public base::NonThreadSafe {
+ public:
+ typedef base::Callback<void(const base::FilePath&, int, void*)>
+ OpenFileCompletion;
+
+ typedef base::Callback<void(const std::vector<base::FilePath>&, void*)>
+ OpenMultipleFilesCompletion;
+
+ typedef base::Callback<void(const base::FilePath&, int, void*)>
+ SaveFileCompletion;
+
+ typedef base::Callback<void(const base::FilePath&, int, void*)>
+ SelectFolderCompletion;
+
+ typedef base::Callback<void(void*)> FileSelectionCanceled;
+
+ // Initializes a viewer process host to connect to the Metro viewer process
+ // over IPC. The given task runner correspond to a thread on which
+ // IPC::Channel is created and used (e.g. IO thread). Instantly connects to
+ // the viewer process if one is already connected to |ipc_channel_name|; a
+ // viewer can otherwise be launched synchronously via
+ // LaunchViewerAndWaitForConnection().
+ explicit MetroViewerProcessHost(
+ const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner);
+ ~MetroViewerProcessHost() override;
+
+ // Returns the process id of the viewer process if one is connected to this
+ // host, returns base::kNullProcessId otherwise.
+ base::ProcessId GetViewerProcessId();
+
+ // Launches the viewer process associated with the given |app_user_model_id|
+ // and blocks until that viewer process connects or until a timeout is
+ // reached. Returns true if the viewer process connects before the timeout is
+ // reached. NOTE: this assumes that the app referred to by |app_user_model_id|
+ // is registered as the default browser.
+ bool LaunchViewerAndWaitForConnection(
+ const base::string16& app_user_model_id);
+
+ // Handles the activate desktop command for Metro Chrome Ash. The |ash_exit|
+ // parameter indicates whether the Ash process would be shutdown after
+ // activating the desktop.
+ static void HandleActivateDesktop(const base::FilePath& shortcut,
+ bool ash_exit);
+
+ // Handles the metro exit command. Notifies the metro viewer to shutdown
+ // gracefully.
+ static void HandleMetroExit();
+
+ // Handles the open file operation for Metro Chrome Ash. The on_success
+ // callback passed in is invoked when we receive the opened file name from
+ // the metro viewer. The on failure callback is invoked on failure.
+ static void HandleOpenFile(const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ const OpenFileCompletion& on_success,
+ const FileSelectionCanceled& on_failure);
+
+ // Handles the open multiple file operation for Metro Chrome Ash. The
+ // on_success callback passed in is invoked when we receive the opened file
+ // names from the metro viewer. The on failure callback is invoked on failure.
+ static void HandleOpenMultipleFiles(
+ const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ const OpenMultipleFilesCompletion& on_success,
+ const FileSelectionCanceled& on_failure);
+
+ // Handles the save file operation for Metro Chrome Ash. The on_success
+ // callback passed in is invoked when we receive the saved file name from
+ // the metro viewer. The on failure callback is invoked on failure.
+ static void HandleSaveFile(const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ int filter_index,
+ const base::string16& default_extension,
+ const SaveFileCompletion& on_success,
+ const FileSelectionCanceled& on_failure);
+
+ // Handles the select folder for Metro Chrome Ash. The on_success
+ // callback passed in is invoked when we receive the folder name from the
+ // metro viewer. The on failure callback is invoked on failure.
+ static void HandleSelectFolder(const base::string16& title,
+ const SelectFolderCompletion& on_success,
+ const FileSelectionCanceled& on_failure);
+
+ protected:
+ // IPC::Sender implementation:
+ bool Send(IPC::Message* msg) override;
+
+ // IPC::Listener implementation:
+ bool OnMessageReceived(const IPC::Message& message) override;
+ void OnChannelError() override = 0;
+
+ private:
+ // The following are the implementation for the corresponding static methods
+ // above, see them for descriptions.
+ void HandleOpenFileImpl(const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ const OpenFileCompletion& on_success,
+ const FileSelectionCanceled& on_failure);
+ void HandleOpenMultipleFilesImpl(
+ const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ const OpenMultipleFilesCompletion& on_success,
+ const FileSelectionCanceled& on_failure);
+ void HandleSaveFileImpl(const base::string16& title,
+ const base::FilePath& default_path,
+ const base::string16& filter,
+ int filter_index,
+ const base::string16& default_extension,
+ const SaveFileCompletion& on_success,
+ const FileSelectionCanceled& on_failure);
+ void HandleSelectFolderImpl(const base::string16& title,
+ const SelectFolderCompletion& on_success,
+ const FileSelectionCanceled& on_failure);
+
+ // Called over IPC by the viewer process to tell this host that it should be
+ // drawing to |target_surface|.
+ virtual void OnSetTargetSurface(gfx::NativeViewId target_surface,
+ float device_scale) = 0;
+
+ // Called over IPC by the viewer process to request that the url passed in be
+ // opened.
+ virtual void OnOpenURL(const base::string16& url) = 0;
+
+ // Called over IPC by the viewer process to request that the search string
+ // passed in is passed to the default search provider and a URL navigation be
+ // performed.
+ virtual void OnHandleSearchRequest(const base::string16& search_string) = 0;
+
+ // Called over IPC by the viewer process when the window size has changed.
+ virtual void OnWindowSizeChanged(uint32_t width, uint32_t height) = 0;
+
+ void NotifyChannelConnected();
+
+ // IPC message handing methods:
+ void OnFileSaveAsDone(bool success,
+ const base::FilePath& filename,
+ int filter_index);
+ void OnFileOpenDone(bool success, const base::FilePath& filename);
+ void OnMultiFileOpenDone(bool success,
+ const std::vector<base::FilePath>& files);
+ void OnSelectFolderDone(bool success, const base::FilePath& folder);
+
+ // Inner message filter used to handle connection event on the IPC channel
+ // proxy's background thread. This prevents consumers of
+ // MetroViewerProcessHost from having to pump messages on their own message
+ // loop.
+ class InternalMessageFilter : public IPC::MessageFilter {
+ public:
+ InternalMessageFilter(MetroViewerProcessHost* owner);
+
+ // IPC::MessageFilter implementation.
+ void OnChannelConnected(int32_t peer_pid) override;
+
+ private:
+ ~InternalMessageFilter() override;
+
+ MetroViewerProcessHost* owner_;
+ DISALLOW_COPY_AND_ASSIGN(InternalMessageFilter);
+ };
+
+ scoped_ptr<IPC::ChannelProxy> channel_;
+ scoped_ptr<base::WaitableEvent> channel_connected_event_;
+ scoped_refptr<InternalMessageFilter> message_filter_;
+
+ static MetroViewerProcessHost* instance_;
+
+ // Saved callbacks which inform the caller about the result of the open file/
+ // save file/select operations.
+ OpenFileCompletion file_open_completion_callback_;
+ OpenMultipleFilesCompletion multi_file_open_completion_callback_;
+ SaveFileCompletion file_saveas_completion_callback_;
+ SelectFolderCompletion select_folder_completion_callback_;
+ FileSelectionCanceled failure_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(MetroViewerProcessHost);
+};
+
+} // namespace win8
+
+#endif // WIN8_VIEWER_METRO_VIEWER_PROCESS_HOST_H_
diff --git a/win8/win8.gyp b/win8/win8.gyp
index 8131fa3..4c68624 100644
--- a/win8/win8.gyp
+++ b/win8/win8.gyp
@@ -10,6 +10,35 @@
],
'targets': [
{
+ 'target_name': 'metro_viewer_constants',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'viewer/metro_viewer_constants.cc',
+ 'viewer/metro_viewer_constants.h',
+ ],
+ },
+ {
+ 'target_name': 'metro_viewer',
+ 'type': '<(component)',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../ipc/ipc.gyp:ipc',
+ '../ui/aura/aura.gyp:aura',
+ '../ui/metro_viewer/metro_viewer.gyp:metro_viewer_messages',
+ 'metro_viewer_constants'
+ ],
+ 'sources': [
+ 'viewer/metro_viewer_process_host.cc',
+ 'viewer/metro_viewer_process_host.h',
+ ],
+ 'defines': [
+ 'METRO_VIEWER_IMPLEMENTATION',
+ ],
+ },
+ {
'target_name': 'test_support_win8',
'type': 'static_library',
'dependencies': [