diff options
author | samli <samli@chromium.org> | 2016-01-19 19:44:56 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-01-20 03:46:11 +0000 |
commit | 0e3a9fd80a26f1fce889200d330d79e64d7a2586 (patch) | |
tree | b81e8346effefb053d8ad102efd2a31c0507bd13 /win8 | |
parent | 15d94b7c0e0b17e973d144c03c7f69f9e0f62309 (diff) | |
download | chromium_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.gn | 25 | ||||
-rw-r--r-- | win8/delegate_execute/BUILD.gn | 4 | ||||
-rw-r--r-- | win8/delegate_execute/command_execute_impl.cc | 15 | ||||
-rw-r--r-- | win8/delegate_execute/delegate_execute.gyp | 5 | ||||
-rw-r--r-- | win8/metro_driver/BUILD.gn | 10 | ||||
-rw-r--r-- | win8/metro_driver/chrome_app_view_ash.cc | 1464 | ||||
-rw-r--r-- | win8/metro_driver/chrome_app_view_ash.h | 262 | ||||
-rw-r--r-- | win8/metro_driver/file_picker.cc | 622 | ||||
-rw-r--r-- | win8/metro_driver/file_picker.h | 18 | ||||
-rw-r--r-- | win8/metro_driver/file_picker_ash.cc | 619 | ||||
-rw-r--r-- | win8/metro_driver/file_picker_ash.h | 157 | ||||
-rw-r--r-- | win8/metro_driver/metro_driver.cc | 135 | ||||
-rw-r--r-- | win8/metro_driver/metro_driver.gyp | 12 | ||||
-rw-r--r-- | win8/metro_driver/metro_driver.h | 17 | ||||
-rw-r--r-- | win8/metro_driver/metro_driver_win7.cc | 1229 | ||||
-rw-r--r-- | win8/viewer/metro_viewer_constants.cc | 13 | ||||
-rw-r--r-- | win8/viewer/metro_viewer_constants.h | 20 | ||||
-rw-r--r-- | win8/viewer/metro_viewer_exports.h | 29 | ||||
-rw-r--r-- | win8/viewer/metro_viewer_process_host.cc | 346 | ||||
-rw-r--r-- | win8/viewer/metro_viewer_process_host.h | 221 | ||||
-rw-r--r-- | win8/win8.gyp | 29 |
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(×tamp_); + 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': [ |