1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "win8/metro_driver/stdafx.h" 6 #include "win8/metro_driver/chrome_app_view_ash.h" 7 8 #include <corewindow.h> 9 #include <shellapi.h> 10 #include <windows.foundation.h> 11 12 #include "base/bind.h" 13 #include "base/command_line.h" 14 #include "base/files/file_path.h" 15 #include "base/message_loop/message_loop.h" 16 #include "base/path_service.h" 17 #include "base/threading/thread.h" 18 #include "base/win/metro.h" 19 #include "base/win/win_util.h" 20 #include "base/win/windows_version.h" 21 #include "chrome/common/chrome_switches.h" 22 #include "ipc/ipc_channel.h" 23 #include "ipc/ipc_channel_proxy.h" 24 #include "ipc/ipc_sender.h" 25 #include "ui/events/gestures/gesture_sequence.h" 26 #include "ui/gfx/geometry/point_conversions.h" 27 #include "ui/gfx/win/dpi.h" 28 #include "ui/metro_viewer/metro_viewer_messages.h" 29 #include "win8/metro_driver/file_picker_ash.h" 30 #include "win8/metro_driver/ime/ime_popup_monitor.h" 31 #include "win8/metro_driver/ime/input_source.h" 32 #include "win8/metro_driver/ime/text_service.h" 33 #include "win8/metro_driver/metro_driver.h" 34 #include "win8/metro_driver/winrt_utils.h" 35 #include "win8/viewer/metro_viewer_constants.h" 36 37 typedef winfoundtn::ITypedEventHandler< 38 winapp::Core::CoreApplicationView*, 39 winapp::Activation::IActivatedEventArgs*> ActivatedHandler; 40 41 typedef winfoundtn::ITypedEventHandler< 42 winui::Core::CoreWindow*, 43 winui::Core::PointerEventArgs*> PointerEventHandler; 44 45 typedef winfoundtn::ITypedEventHandler< 46 winui::Core::CoreWindow*, 47 winui::Core::KeyEventArgs*> KeyEventHandler; 48 49 typedef winfoundtn::ITypedEventHandler< 50 winui::Core::CoreDispatcher*, 51 winui::Core::AcceleratorKeyEventArgs*> AcceleratorKeyEventHandler; 52 53 typedef winfoundtn::ITypedEventHandler< 54 winui::Core::CoreWindow*, 55 winui::Core::CharacterReceivedEventArgs*> CharEventHandler; 56 57 typedef winfoundtn::ITypedEventHandler< 58 winui::Core::CoreWindow*, 59 winui::Core::WindowActivatedEventArgs*> WindowActivatedHandler; 60 61 typedef winfoundtn::ITypedEventHandler< 62 winui::Core::CoreWindow*, 63 winui::Core::WindowSizeChangedEventArgs*> SizeChangedHandler; 64 65 typedef winfoundtn::ITypedEventHandler< 66 winui::Input::EdgeGesture*, 67 winui::Input::EdgeGestureEventArgs*> EdgeEventHandler; 68 69 // This function is exported by chrome.exe. 70 typedef int (__cdecl *BreakpadExceptionHandler)(EXCEPTION_POINTERS* info); 71 72 // Global information used across the metro driver. 73 struct Globals { 74 winapp::Activation::ApplicationExecutionState previous_state; 75 winapp::Core::ICoreApplicationExit* app_exit; 76 BreakpadExceptionHandler breakpad_exception_handler; 77 } globals; 78 79 extern float GetModernUIScale(); 80 81 namespace { 82 83 enum KeyModifier { 84 NONE, 85 SHIFT = 1, 86 CONTROL = 2, 87 ALT = 4 88 }; 89 90 // Helper function to send keystrokes via the SendInput function. 91 // mnemonic_char: The keystroke to be sent. 92 // modifiers: Combination with Alt, Ctrl, Shift, etc. 93 void SendKeySequence( 94 WORD mnemonic_char, KeyModifier modifiers) { 95 INPUT keys[4] = {0}; // Keyboard events 96 int key_count = 0; // Number of generated events 97 98 if (modifiers & SHIFT) { 99 keys[key_count].type = INPUT_KEYBOARD; 100 keys[key_count].ki.wVk = VK_SHIFT; 101 keys[key_count].ki.wScan = MapVirtualKey(VK_SHIFT, 0); 102 key_count++; 103 } 104 105 if (modifiers & CONTROL) { 106 keys[key_count].type = INPUT_KEYBOARD; 107 keys[key_count].ki.wVk = VK_CONTROL; 108 keys[key_count].ki.wScan = MapVirtualKey(VK_CONTROL, 0); 109 key_count++; 110 } 111 112 if (modifiers & ALT) { 113 keys[key_count].type = INPUT_KEYBOARD; 114 keys[key_count].ki.wVk = VK_MENU; 115 keys[key_count].ki.wScan = MapVirtualKey(VK_MENU, 0); 116 key_count++; 117 } 118 119 keys[key_count].type = INPUT_KEYBOARD; 120 keys[key_count].ki.wVk = mnemonic_char; 121 keys[key_count].ki.wScan = MapVirtualKey(mnemonic_char, 0); 122 key_count++; 123 124 bool should_sleep = key_count > 1; 125 126 // Send key downs. 127 for (int i = 0; i < key_count; i++) { 128 SendInput(1, &keys[ i ], sizeof(keys[0])); 129 keys[i].ki.dwFlags |= KEYEVENTF_KEYUP; 130 if (should_sleep) 131 Sleep(10); 132 } 133 134 // Now send key ups in reverse order. 135 for (int i = key_count; i; i--) { 136 SendInput(1, &keys[ i - 1 ], sizeof(keys[0])); 137 if (should_sleep) 138 Sleep(10); 139 } 140 } 141 142 class ChromeChannelListener : public IPC::Listener { 143 public: 144 ChromeChannelListener(base::MessageLoop* ui_loop, ChromeAppViewAsh* app_view) 145 : ui_proxy_(ui_loop->message_loop_proxy()), 146 app_view_(app_view) {} 147 148 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { 149 IPC_BEGIN_MESSAGE_MAP(ChromeChannelListener, message) 150 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_ActivateDesktop, 151 OnActivateDesktop) 152 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_MetroExit, OnMetroExit) 153 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_OpenURLOnDesktop, 154 OnOpenURLOnDesktop) 155 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SetCursor, OnSetCursor) 156 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_DisplayFileOpen, 157 OnDisplayFileOpenDialog) 158 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_DisplayFileSaveAs, 159 OnDisplayFileSaveAsDialog) 160 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_DisplaySelectFolder, 161 OnDisplayFolderPicker) 162 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SetCursorPos, OnSetCursorPos) 163 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_ImeCancelComposition, 164 OnImeCancelComposition) 165 IPC_MESSAGE_HANDLER(MetroViewerHostMsg_ImeTextInputClientUpdated, 166 OnImeTextInputClientChanged) 167 IPC_MESSAGE_UNHANDLED(__debugbreak()) 168 IPC_END_MESSAGE_MAP() 169 return true; 170 } 171 172 virtual void OnChannelError() OVERRIDE { 173 DVLOG(1) << "Channel error. Exiting."; 174 ui_proxy_->PostTask(FROM_HERE, 175 base::Bind(&ChromeAppViewAsh::OnMetroExit, base::Unretained(app_view_), 176 TERMINATE_USING_KEY_SEQUENCE)); 177 178 // In early Windows 8 versions the code above sometimes fails so we call 179 // it a second time with a NULL window which just calls Exit(). 180 ui_proxy_->PostDelayedTask(FROM_HERE, 181 base::Bind(&ChromeAppViewAsh::OnMetroExit, base::Unretained(app_view_), 182 TERMINATE_USING_PROCESS_EXIT), 183 base::TimeDelta::FromMilliseconds(100)); 184 } 185 186 private: 187 void OnActivateDesktop(const base::FilePath& shortcut, bool ash_exit) { 188 ui_proxy_->PostTask(FROM_HERE, 189 base::Bind(&ChromeAppViewAsh::OnActivateDesktop, 190 base::Unretained(app_view_), 191 shortcut, ash_exit)); 192 } 193 194 void OnMetroExit() { 195 ui_proxy_->PostTask(FROM_HERE, 196 base::Bind(&ChromeAppViewAsh::OnMetroExit, 197 base::Unretained(app_view_), TERMINATE_USING_KEY_SEQUENCE)); 198 } 199 200 void OnOpenURLOnDesktop(const base::FilePath& shortcut, 201 const base::string16& url) { 202 ui_proxy_->PostTask(FROM_HERE, 203 base::Bind(&ChromeAppViewAsh::OnOpenURLOnDesktop, 204 base::Unretained(app_view_), 205 shortcut, url)); 206 } 207 208 void OnSetCursor(int64 cursor) { 209 ui_proxy_->PostTask(FROM_HERE, 210 base::Bind(&ChromeAppViewAsh::OnSetCursor, 211 base::Unretained(app_view_), 212 reinterpret_cast<HCURSOR>(cursor))); 213 } 214 215 void OnDisplayFileOpenDialog(const base::string16& title, 216 const base::string16& filter, 217 const base::FilePath& default_path, 218 bool allow_multiple_files) { 219 ui_proxy_->PostTask(FROM_HERE, 220 base::Bind(&ChromeAppViewAsh::OnDisplayFileOpenDialog, 221 base::Unretained(app_view_), 222 title, 223 filter, 224 default_path, 225 allow_multiple_files)); 226 } 227 228 void OnDisplayFileSaveAsDialog( 229 const MetroViewerHostMsg_SaveAsDialogParams& params) { 230 ui_proxy_->PostTask( 231 FROM_HERE, 232 base::Bind(&ChromeAppViewAsh::OnDisplayFileSaveAsDialog, 233 base::Unretained(app_view_), 234 params)); 235 } 236 237 void OnDisplayFolderPicker(const base::string16& title) { 238 ui_proxy_->PostTask( 239 FROM_HERE, 240 base::Bind(&ChromeAppViewAsh::OnDisplayFolderPicker, 241 base::Unretained(app_view_), 242 title)); 243 } 244 245 void OnSetCursorPos(int x, int y) { 246 VLOG(1) << "In IPC OnSetCursorPos: " << x << ", " << y; 247 ui_proxy_->PostTask( 248 FROM_HERE, 249 base::Bind(&ChromeAppViewAsh::OnSetCursorPos, 250 base::Unretained(app_view_), 251 x, y)); 252 } 253 254 void OnImeCancelComposition() { 255 ui_proxy_->PostTask( 256 FROM_HERE, 257 base::Bind(&ChromeAppViewAsh::OnImeCancelComposition, 258 base::Unretained(app_view_))); 259 } 260 261 void OnImeTextInputClientChanged( 262 const std::vector<int32>& input_scopes, 263 const std::vector<metro_viewer::CharacterBounds>& character_bounds) { 264 ui_proxy_->PostTask( 265 FROM_HERE, 266 base::Bind(&ChromeAppViewAsh::OnImeUpdateTextInputClient, 267 base::Unretained(app_view_), 268 input_scopes, 269 character_bounds)); 270 } 271 272 scoped_refptr<base::MessageLoopProxy> ui_proxy_; 273 ChromeAppViewAsh* app_view_; 274 }; 275 276 bool WaitForChromeIPCConnection(const std::string& channel_name) { 277 int ms_elapsed = 0; 278 while (!IPC::Channel::IsNamedServerInitialized(channel_name) && 279 ms_elapsed < 10000) { 280 ms_elapsed += 100; 281 Sleep(100); 282 } 283 return IPC::Channel::IsNamedServerInitialized(channel_name); 284 } 285 286 void RunMessageLoop(winui::Core::ICoreDispatcher* dispatcher) { 287 // We're entering a nested message loop, let's allow dispatching 288 // tasks while we're in there. 289 base::MessageLoop::current()->SetNestableTasksAllowed(true); 290 291 // Enter main core message loop. There are several ways to exit it 292 // Nicely: 293 // 1 - User action like ALT-F4. 294 // 2 - Calling ICoreApplicationExit::Exit(). 295 // 3- Posting WM_CLOSE to the core window. 296 HRESULT hr = dispatcher->ProcessEvents( 297 winui::Core::CoreProcessEventsOption 298 ::CoreProcessEventsOption_ProcessUntilQuit); 299 300 // Wind down the thread's chrome message loop. 301 base::MessageLoop::current()->Quit(); 302 } 303 304 // Helper to return the state of the shift/control/alt keys. 305 uint32 GetKeyboardEventFlags() { 306 uint32 flags = 0; 307 if (base::win::IsShiftPressed()) 308 flags |= ui::EF_SHIFT_DOWN; 309 if (base::win::IsCtrlPressed()) 310 flags |= ui::EF_CONTROL_DOWN; 311 if (base::win::IsAltPressed()) 312 flags |= ui::EF_ALT_DOWN; 313 return flags; 314 } 315 316 bool LaunchChromeBrowserProcess(const wchar_t* additional_parameters, 317 winapp::Activation::IActivatedEventArgs* args) { 318 if (args) { 319 DVLOG(1) << __FUNCTION__ << ":" << ::GetCommandLineW(); 320 winapp::Activation::ActivationKind activation_kind; 321 CheckHR(args->get_Kind(&activation_kind)); 322 323 DVLOG(1) << __FUNCTION__ << ", activation_kind=" << activation_kind; 324 325 if (activation_kind == winapp::Activation::ActivationKind_Launch) { 326 mswr::ComPtr<winapp::Activation::ILaunchActivatedEventArgs> launch_args; 327 if (args->QueryInterface( 328 winapp::Activation::IID_ILaunchActivatedEventArgs, 329 &launch_args) == S_OK) { 330 DVLOG(1) << "Activate: ActivationKind_Launch"; 331 mswrw::HString launch_args_str; 332 launch_args->get_Arguments(launch_args_str.GetAddressOf()); 333 base::string16 actual_launch_args( 334 MakeStdWString(launch_args_str.Get())); 335 if (actual_launch_args == win8::kMetroViewerConnectVerb) { 336 DVLOG(1) << __FUNCTION__ << "Not launching chrome server"; 337 return true; 338 } 339 } 340 } 341 } 342 343 DVLOG(1) << "Launching chrome server"; 344 base::FilePath chrome_exe_path; 345 346 if (!PathService::Get(base::FILE_EXE, &chrome_exe_path)) 347 return false; 348 349 base::string16 parameters = L"--silent-launch --viewer-connect "; 350 if (additional_parameters) 351 parameters += additional_parameters; 352 353 SHELLEXECUTEINFO sei = { sizeof(sei) }; 354 sei.nShow = SW_SHOWNORMAL; 355 sei.lpFile = chrome_exe_path.value().c_str(); 356 sei.lpDirectory = L""; 357 sei.lpParameters = parameters.c_str(); 358 ::ShellExecuteEx(&sei); 359 return true; 360 } 361 362 } // namespace 363 364 // This class helps decoding the pointer properties of an event. 365 class ChromeAppViewAsh::PointerInfoHandler { 366 public: 367 PointerInfoHandler(float metro_dpi_scale, float win32_dpi_scale) 368 : x_(0), 369 y_(0), 370 wheel_delta_(0), 371 update_kind_(winui::Input::PointerUpdateKind_Other), 372 timestamp_(0), 373 pointer_id_(0), 374 mouse_down_flags_(0), 375 is_horizontal_wheel_(0), 376 metro_dpi_scale_(metro_dpi_scale), 377 win32_dpi_scale_(win32_dpi_scale) {} 378 379 HRESULT Init(winui::Core::IPointerEventArgs* args) { 380 HRESULT hr = args->get_CurrentPoint(&pointer_point_); 381 if (FAILED(hr)) 382 return hr; 383 384 winfoundtn::Point point; 385 hr = pointer_point_->get_Position(&point); 386 if (FAILED(hr)) 387 return hr; 388 389 mswr::ComPtr<winui::Input::IPointerPointProperties> properties; 390 hr = pointer_point_->get_Properties(&properties); 391 if (FAILED(hr)) 392 return hr; 393 394 hr = properties->get_PointerUpdateKind(&update_kind_); 395 if (FAILED(hr)) 396 return hr; 397 398 hr = properties->get_MouseWheelDelta(&wheel_delta_); 399 if (FAILED(hr)) 400 return hr; 401 402 is_horizontal_wheel_ = 0; 403 properties->get_IsHorizontalMouseWheel(&is_horizontal_wheel_); 404 405 // The input coordinates are in DIP based on the metro scale factor. 406 // We want to convert it to DIP based on the win32 scale factor. 407 // We scale the point by the metro scale factor and then scale down 408 // via the win32 scale factor which achieves the needful. 409 gfx::Point dip_point_metro(point.X, point.Y); 410 gfx::Point scaled_point_metro = 411 gfx::ToCeiledPoint(gfx::ScalePoint(dip_point_metro, metro_dpi_scale_)); 412 gfx::Point dip_point_win32 = 413 gfx::ToCeiledPoint(gfx::ScalePoint(scaled_point_metro, 414 1.0 / win32_dpi_scale_)); 415 x_ = dip_point_win32.x(); 416 y_ = dip_point_win32.y(); 417 418 pointer_point_->get_Timestamp(×tamp_); 419 pointer_point_->get_PointerId(&pointer_id_); 420 // Map the OS touch event id to a range allowed by the gesture recognizer. 421 if (IsTouch()) 422 pointer_id_ %= ui::GestureSequence::kMaxGesturePoints; 423 424 boolean left_button_state; 425 hr = properties->get_IsLeftButtonPressed(&left_button_state); 426 if (FAILED(hr)) 427 return hr; 428 if (left_button_state) 429 mouse_down_flags_ |= ui::EF_LEFT_MOUSE_BUTTON; 430 431 boolean right_button_state; 432 hr = properties->get_IsRightButtonPressed(&right_button_state); 433 if (FAILED(hr)) 434 return hr; 435 if (right_button_state) 436 mouse_down_flags_ |= ui::EF_RIGHT_MOUSE_BUTTON; 437 438 boolean middle_button_state; 439 hr = properties->get_IsMiddleButtonPressed(&middle_button_state); 440 if (FAILED(hr)) 441 return hr; 442 if (middle_button_state) 443 mouse_down_flags_ |= ui::EF_MIDDLE_MOUSE_BUTTON; 444 445 return S_OK; 446 } 447 448 bool IsType(windevs::Input::PointerDeviceType type) const { 449 mswr::ComPtr<windevs::Input::IPointerDevice> pointer_device; 450 CheckHR(pointer_point_->get_PointerDevice(&pointer_device)); 451 windevs::Input::PointerDeviceType device_type; 452 CheckHR(pointer_device->get_PointerDeviceType(&device_type)); 453 return (device_type == type); 454 } 455 456 bool IsMouse() const { 457 return IsType(windevs::Input::PointerDeviceType_Mouse); 458 } 459 460 bool IsTouch() const { 461 return IsType(windevs::Input::PointerDeviceType_Touch); 462 } 463 464 int32 wheel_delta() const { 465 return wheel_delta_; 466 } 467 468 // Identifies the button that changed. 469 ui::EventFlags changed_button() const { 470 switch (update_kind_) { 471 case winui::Input::PointerUpdateKind_LeftButtonPressed: 472 return ui::EF_LEFT_MOUSE_BUTTON; 473 case winui::Input::PointerUpdateKind_LeftButtonReleased: 474 return ui::EF_LEFT_MOUSE_BUTTON; 475 case winui::Input::PointerUpdateKind_RightButtonPressed: 476 return ui::EF_RIGHT_MOUSE_BUTTON; 477 case winui::Input::PointerUpdateKind_RightButtonReleased: 478 return ui::EF_RIGHT_MOUSE_BUTTON; 479 case winui::Input::PointerUpdateKind_MiddleButtonPressed: 480 return ui::EF_MIDDLE_MOUSE_BUTTON; 481 case winui::Input::PointerUpdateKind_MiddleButtonReleased: 482 return ui::EF_MIDDLE_MOUSE_BUTTON; 483 default: 484 return ui::EF_NONE; 485 } 486 } 487 488 uint32 mouse_down_flags() const { return mouse_down_flags_; } 489 490 int x() const { return x_; } 491 int y() const { return y_; } 492 493 uint32 pointer_id() const { 494 return pointer_id_; 495 } 496 497 uint64 timestamp() const { return timestamp_; } 498 499 winui::Input::PointerUpdateKind update_kind() const { return update_kind_; } 500 501 bool is_horizontal_wheel() const { return !!is_horizontal_wheel_; } 502 503 private: 504 int x_; 505 int y_; 506 int wheel_delta_; 507 uint32 pointer_id_; 508 winui::Input::PointerUpdateKind update_kind_; 509 mswr::ComPtr<winui::Input::IPointerPoint> pointer_point_; 510 uint64 timestamp_; 511 512 // Bitmask of ui::EventFlags corresponding to the buttons that are currently 513 // down. 514 uint32 mouse_down_flags_; 515 516 // Set to true for a horizontal wheel message. 517 boolean is_horizontal_wheel_; 518 519 // The metro device scale factor as reported by the winrt interfaces. 520 float metro_dpi_scale_; 521 // The win32 dpi scale which is queried via GetDeviceCaps. Please refer to 522 // ui/gfx/win/dpi.cc for more information. 523 float win32_dpi_scale_; 524 525 DISALLOW_COPY_AND_ASSIGN(PointerInfoHandler); 526 }; 527 528 ChromeAppViewAsh::ChromeAppViewAsh() 529 : mouse_down_flags_(ui::EF_NONE), 530 ui_channel_(nullptr), 531 core_window_hwnd_(NULL), 532 metro_dpi_scale_(0), 533 win32_dpi_scale_(0) { 534 DVLOG(1) << __FUNCTION__; 535 globals.previous_state = 536 winapp::Activation::ApplicationExecutionState_NotRunning; 537 } 538 539 ChromeAppViewAsh::~ChromeAppViewAsh() { 540 DVLOG(1) << __FUNCTION__; 541 } 542 543 IFACEMETHODIMP 544 ChromeAppViewAsh::Initialize(winapp::Core::ICoreApplicationView* view) { 545 view_ = view; 546 DVLOG(1) << __FUNCTION__; 547 HRESULT hr = view_->add_Activated(mswr::Callback<ActivatedHandler>( 548 this, &ChromeAppViewAsh::OnActivate).Get(), 549 &activated_token_); 550 CheckHR(hr); 551 return hr; 552 } 553 554 IFACEMETHODIMP 555 ChromeAppViewAsh::SetWindow(winui::Core::ICoreWindow* window) { 556 window_ = window; 557 DVLOG(1) << __FUNCTION__; 558 559 // Retrieve the native window handle via the interop layer. 560 mswr::ComPtr<ICoreWindowInterop> interop; 561 HRESULT hr = window->QueryInterface(interop.GetAddressOf()); 562 CheckHR(hr); 563 hr = interop->get_WindowHandle(&core_window_hwnd_); 564 CheckHR(hr); 565 566 text_service_ = metro_driver::CreateTextService(this, core_window_hwnd_); 567 568 hr = window_->add_SizeChanged(mswr::Callback<SizeChangedHandler>( 569 this, &ChromeAppViewAsh::OnSizeChanged).Get(), 570 &sizechange_token_); 571 CheckHR(hr); 572 573 // Register for pointer and keyboard notifications. We forward 574 // them to the browser process via IPC. 575 hr = window_->add_PointerMoved(mswr::Callback<PointerEventHandler>( 576 this, &ChromeAppViewAsh::OnPointerMoved).Get(), 577 &pointermoved_token_); 578 CheckHR(hr); 579 580 hr = window_->add_PointerPressed(mswr::Callback<PointerEventHandler>( 581 this, &ChromeAppViewAsh::OnPointerPressed).Get(), 582 &pointerpressed_token_); 583 CheckHR(hr); 584 585 hr = window_->add_PointerReleased(mswr::Callback<PointerEventHandler>( 586 this, &ChromeAppViewAsh::OnPointerReleased).Get(), 587 &pointerreleased_token_); 588 CheckHR(hr); 589 590 hr = window_->add_KeyDown(mswr::Callback<KeyEventHandler>( 591 this, &ChromeAppViewAsh::OnKeyDown).Get(), 592 &keydown_token_); 593 CheckHR(hr); 594 595 hr = window_->add_KeyUp(mswr::Callback<KeyEventHandler>( 596 this, &ChromeAppViewAsh::OnKeyUp).Get(), 597 &keyup_token_); 598 CheckHR(hr); 599 600 mswr::ComPtr<winui::Core::ICoreDispatcher> dispatcher; 601 hr = window_->get_Dispatcher(dispatcher.GetAddressOf()); 602 CheckHR(hr, "Get Dispatcher failed."); 603 604 mswr::ComPtr<winui::Core::ICoreAcceleratorKeys> accelerator_keys; 605 hr = dispatcher.CopyTo(__uuidof(winui::Core::ICoreAcceleratorKeys), 606 reinterpret_cast<void**>( 607 accelerator_keys.GetAddressOf())); 608 CheckHR(hr, "QI for ICoreAcceleratorKeys failed."); 609 hr = accelerator_keys->add_AcceleratorKeyActivated( 610 mswr::Callback<AcceleratorKeyEventHandler>( 611 this, &ChromeAppViewAsh::OnAcceleratorKeyDown).Get(), 612 &accel_keydown_token_); 613 CheckHR(hr); 614 615 hr = window_->add_PointerWheelChanged(mswr::Callback<PointerEventHandler>( 616 this, &ChromeAppViewAsh::OnWheel).Get(), 617 &wheel_token_); 618 CheckHR(hr); 619 620 hr = window_->add_CharacterReceived(mswr::Callback<CharEventHandler>( 621 this, &ChromeAppViewAsh::OnCharacterReceived).Get(), 622 &character_received_token_); 623 CheckHR(hr); 624 625 hr = window_->add_Activated(mswr::Callback<WindowActivatedHandler>( 626 this, &ChromeAppViewAsh::OnWindowActivated).Get(), 627 &window_activated_token_); 628 CheckHR(hr); 629 630 if (base::win::GetVersion() >= base::win::VERSION_WIN8) { 631 // Register for edge gesture notifications only for Windows 8 and above. 632 mswr::ComPtr<winui::Input::IEdgeGestureStatics> edge_gesture_statics; 633 hr = winrt_utils::CreateActivationFactory( 634 RuntimeClass_Windows_UI_Input_EdgeGesture, 635 edge_gesture_statics.GetAddressOf()); 636 CheckHR(hr); 637 638 mswr::ComPtr<winui::Input::IEdgeGesture> edge_gesture; 639 hr = edge_gesture_statics->GetForCurrentView(&edge_gesture); 640 CheckHR(hr); 641 642 hr = edge_gesture->add_Completed(mswr::Callback<EdgeEventHandler>( 643 this, &ChromeAppViewAsh::OnEdgeGestureCompleted).Get(), 644 &edgeevent_token_); 645 CheckHR(hr); 646 } 647 648 // By initializing the direct 3D swap chain with the corewindow 649 // we can now directly blit to it from the browser process. 650 direct3d_helper_.Initialize(window); 651 DVLOG(1) << "Initialized Direct3D."; 652 653 // On Windows 8+ the WinRT interface IDisplayProperties which we use to get 654 // device scale factor does not return the correct values in metro mode. 655 // To workaround this we retrieve the device scale factor via the win32 way 656 // and scale input coordinates accordingly to pass them in DIP to chrome. 657 // TODO(ananta). Investigate and fix. 658 metro_dpi_scale_ = GetModernUIScale(); 659 win32_dpi_scale_ = gfx::GetDPIScale(); 660 DVLOG(1) << "Metro Scale is " << metro_dpi_scale_; 661 DVLOG(1) << "Win32 Scale is " << win32_dpi_scale_; 662 return S_OK; 663 } 664 665 IFACEMETHODIMP 666 ChromeAppViewAsh::Load(HSTRING entryPoint) { 667 // On Win7 |entryPoint| is NULL. 668 DVLOG(1) << __FUNCTION__; 669 return S_OK; 670 } 671 672 IFACEMETHODIMP 673 ChromeAppViewAsh::Run() { 674 DVLOG(1) << __FUNCTION__; 675 mswr::ComPtr<winui::Core::ICoreDispatcher> dispatcher; 676 HRESULT hr = window_->get_Dispatcher(dispatcher.GetAddressOf()); 677 CheckHR(hr, "Dispatcher failed."); 678 679 hr = window_->Activate(); 680 if (FAILED(hr)) { 681 DLOG(WARNING) << "activation failed hr=" << hr; 682 return hr; 683 } 684 685 // Create the IPC channel IO thread. It needs to out-live the ChannelProxy. 686 base::Thread io_thread("metro_IO_thread"); 687 base::Thread::Options options; 688 options.message_loop_type = base::MessageLoop::TYPE_IO; 689 io_thread.StartWithOptions(options); 690 691 // Start up Chrome and wait for the desired IPC server connection to exist. 692 WaitForChromeIPCConnection(win8::kMetroViewerIPCChannelName); 693 694 // In Aura mode we create an IPC channel to the browser, then ask it to 695 // connect to us. 696 ChromeChannelListener ui_channel_listener(&ui_loop_, this); 697 scoped_ptr<IPC::ChannelProxy> channel = 698 IPC::ChannelProxy::Create(win8::kMetroViewerIPCChannelName, 699 IPC::Channel::MODE_NAMED_CLIENT, 700 &ui_channel_listener, 701 io_thread.message_loop_proxy()); 702 ui_channel_ = channel.get(); 703 704 // Upon receipt of the MetroViewerHostMsg_SetTargetSurface message the 705 // browser will use D3D from the browser process to present to our Window. 706 ui_channel_->Send(new MetroViewerHostMsg_SetTargetSurface( 707 gfx::NativeViewId(core_window_hwnd_), win32_dpi_scale_)); 708 DVLOG(1) << "ICoreWindow sent " << core_window_hwnd_; 709 710 // Send an initial size message so that the Ash root window host gets sized 711 // correctly. 712 RECT rect = {0}; 713 ::GetWindowRect(core_window_hwnd_, &rect); 714 ui_channel_->Send( 715 new MetroViewerHostMsg_WindowSizeChanged(rect.right - rect.left, 716 rect.bottom - rect.top)); 717 718 input_source_ = metro_driver::InputSource::Create(); 719 if (input_source_) { 720 input_source_->AddObserver(this); 721 // Send an initial input source. 722 OnInputSourceChanged(); 723 } 724 725 // Start receiving IME popup window notifications. 726 metro_driver::AddImePopupObserver(this); 727 728 // And post the task that'll do the inner Metro message pumping to it. 729 ui_loop_.PostTask(FROM_HERE, base::Bind(&RunMessageLoop, dispatcher.Get())); 730 ui_loop_.Run(); 731 732 DVLOG(0) << "ProcessEvents done, hr=" << hr; 733 return hr; 734 } 735 736 IFACEMETHODIMP 737 ChromeAppViewAsh::Uninitialize() { 738 DVLOG(1) << __FUNCTION__; 739 metro_driver::RemoveImePopupObserver(this); 740 input_source_.reset(); 741 text_service_.reset(); 742 window_ = nullptr; 743 view_ = nullptr; 744 core_window_hwnd_ = NULL; 745 return S_OK; 746 } 747 748 // static 749 HRESULT ChromeAppViewAsh::Unsnap() { 750 mswr::ComPtr<winui::ViewManagement::IApplicationViewStatics> view_statics; 751 HRESULT hr = winrt_utils::CreateActivationFactory( 752 RuntimeClass_Windows_UI_ViewManagement_ApplicationView, 753 view_statics.GetAddressOf()); 754 CheckHR(hr); 755 756 winui::ViewManagement::ApplicationViewState state = 757 winui::ViewManagement::ApplicationViewState_FullScreenLandscape; 758 hr = view_statics->get_Value(&state); 759 CheckHR(hr); 760 761 if (state == winui::ViewManagement::ApplicationViewState_Snapped) { 762 boolean success = FALSE; 763 hr = view_statics->TryUnsnap(&success); 764 765 if (FAILED(hr) || !success) { 766 LOG(ERROR) << "Failed to unsnap. Error 0x" << hr; 767 if (SUCCEEDED(hr)) 768 hr = E_UNEXPECTED; 769 } 770 } 771 return hr; 772 } 773 774 void ChromeAppViewAsh::OnActivateDesktop(const base::FilePath& file_path, 775 bool ash_exit) { 776 DVLOG(1) << "ChannelAppViewAsh::OnActivateDesktop\n"; 777 778 if (ash_exit) { 779 // As we are the top level window, the exiting is done async so we manage 780 // to execute the entire function including the final Send(). 781 OnMetroExit(TERMINATE_USING_KEY_SEQUENCE); 782 } 783 784 // We are just executing delegate_execute here without parameters. Assumption 785 // here is that this process will be reused by shell when asking for 786 // IExecuteCommand interface. 787 788 // TODO(shrikant): Consolidate ShellExecuteEx with SEE_MASK_FLAG_LOG_USAGE 789 // and place it metro.h or similar accessible file from all code code paths 790 // using this function. 791 SHELLEXECUTEINFO sei = { sizeof(sei) }; 792 sei.fMask = SEE_MASK_FLAG_LOG_USAGE; 793 sei.nShow = SW_SHOWNORMAL; 794 sei.lpFile = file_path.value().c_str(); 795 sei.lpParameters = NULL; 796 if (!ash_exit) 797 sei.fMask |= SEE_MASK_NOCLOSEPROCESS; 798 ::ShellExecuteExW(&sei); 799 if (!ash_exit) { 800 ::TerminateProcess(sei.hProcess, 0); 801 ::CloseHandle(sei.hProcess); 802 } 803 } 804 805 void ChromeAppViewAsh::OnOpenURLOnDesktop(const base::FilePath& shortcut, 806 const base::string16& url) { 807 base::FilePath::StringType file = shortcut.value(); 808 SHELLEXECUTEINFO sei = { sizeof(sei) }; 809 sei.fMask = SEE_MASK_FLAG_LOG_USAGE; 810 sei.nShow = SW_SHOWNORMAL; 811 sei.lpFile = file.c_str(); 812 sei.lpDirectory = L""; 813 sei.lpParameters = url.c_str(); 814 BOOL result = ShellExecuteEx(&sei); 815 } 816 817 void ChromeAppViewAsh::OnSetCursor(HCURSOR cursor) { 818 ::SetCursor(HCURSOR(cursor)); 819 } 820 821 void ChromeAppViewAsh::OnDisplayFileOpenDialog( 822 const base::string16& title, 823 const base::string16& filter, 824 const base::FilePath& default_path, 825 bool allow_multiple_files) { 826 DVLOG(1) << __FUNCTION__; 827 828 // The OpenFilePickerSession instance is deleted when we receive a 829 // callback from the OpenFilePickerSession class about the completion of the 830 // operation. 831 FilePickerSessionBase* file_picker_ = 832 new OpenFilePickerSession(this, 833 title, 834 filter, 835 default_path, 836 allow_multiple_files); 837 file_picker_->Run(); 838 } 839 840 void ChromeAppViewAsh::OnDisplayFileSaveAsDialog( 841 const MetroViewerHostMsg_SaveAsDialogParams& params) { 842 DVLOG(1) << __FUNCTION__; 843 844 // The SaveFilePickerSession instance is deleted when we receive a 845 // callback from the SaveFilePickerSession class about the completion of the 846 // operation. 847 FilePickerSessionBase* file_picker_ = 848 new SaveFilePickerSession(this, params); 849 file_picker_->Run(); 850 } 851 852 void ChromeAppViewAsh::OnDisplayFolderPicker(const base::string16& title) { 853 DVLOG(1) << __FUNCTION__; 854 // The FolderPickerSession instance is deleted when we receive a 855 // callback from the FolderPickerSession class about the completion of the 856 // operation. 857 FilePickerSessionBase* file_picker_ = new FolderPickerSession(this, title); 858 file_picker_->Run(); 859 } 860 861 void ChromeAppViewAsh::OnSetCursorPos(int x, int y) { 862 if (ui_channel_) { 863 ::SetCursorPos(x, y); 864 DVLOG(1) << "In UI OnSetCursorPos: " << x << ", " << y; 865 ui_channel_->Send(new MetroViewerHostMsg_SetCursorPosAck()); 866 // Generate a fake mouse move which matches the SetCursor coordinates as 867 // the browser expects to receive a mouse move for these coordinates. 868 // It is not clear why we don't receive a real mouse move in response to 869 // the SetCursorPos calll above. 870 ui_channel_->Send(new MetroViewerHostMsg_MouseMoved(x, y, 0)); 871 } 872 } 873 874 void ChromeAppViewAsh::OnOpenFileCompleted( 875 OpenFilePickerSession* open_file_picker, 876 bool success) { 877 DVLOG(1) << __FUNCTION__; 878 DVLOG(1) << "Success: " << success; 879 if (ui_channel_) { 880 if (open_file_picker->allow_multi_select()) { 881 ui_channel_->Send(new MetroViewerHostMsg_MultiFileOpenDone( 882 success, open_file_picker->filenames())); 883 } else { 884 ui_channel_->Send(new MetroViewerHostMsg_FileOpenDone( 885 success, base::FilePath(open_file_picker->result()))); 886 } 887 } 888 delete open_file_picker; 889 } 890 891 void ChromeAppViewAsh::OnSaveFileCompleted( 892 SaveFilePickerSession* save_file_picker, 893 bool success) { 894 DVLOG(1) << __FUNCTION__; 895 DVLOG(1) << "Success: " << success; 896 if (ui_channel_) { 897 ui_channel_->Send(new MetroViewerHostMsg_FileSaveAsDone( 898 success, 899 base::FilePath(save_file_picker->result()), 900 save_file_picker->filter_index())); 901 } 902 delete save_file_picker; 903 } 904 905 void ChromeAppViewAsh::OnFolderPickerCompleted( 906 FolderPickerSession* folder_picker, 907 bool success) { 908 DVLOG(1) << __FUNCTION__; 909 DVLOG(1) << "Success: " << success; 910 if (ui_channel_) { 911 ui_channel_->Send(new MetroViewerHostMsg_SelectFolderDone( 912 success, 913 base::FilePath(folder_picker->result()))); 914 } 915 delete folder_picker; 916 } 917 918 void ChromeAppViewAsh::OnImeCancelComposition() { 919 if (!text_service_) 920 return; 921 text_service_->CancelComposition(); 922 } 923 924 void ChromeAppViewAsh::OnImeUpdateTextInputClient( 925 const std::vector<int32>& input_scopes, 926 const std::vector<metro_viewer::CharacterBounds>& character_bounds) { 927 if (!text_service_) 928 return; 929 text_service_->OnDocumentChanged(input_scopes, character_bounds); 930 } 931 932 void ChromeAppViewAsh::OnImePopupChanged(ImePopupObserver::EventType event) { 933 if (!ui_channel_) 934 return; 935 switch (event) { 936 case ImePopupObserver::kPopupShown: 937 ui_channel_->Send(new MetroViewerHostMsg_ImeCandidatePopupChanged(true)); 938 return; 939 case ImePopupObserver::kPopupHidden: 940 ui_channel_->Send(new MetroViewerHostMsg_ImeCandidatePopupChanged(false)); 941 return; 942 case ImePopupObserver::kPopupUpdated: 943 // TODO(kochi): Support this event for W3C IME API proposal. 944 // See crbug.com/238585. 945 return; 946 default: 947 NOTREACHED() << "unknown event type: " << event; 948 return; 949 } 950 } 951 952 // Function to Exit metro chrome cleanly. If we are in the foreground 953 // then we try and exit by sending an Alt+F4 key combination to the core 954 // window which ensures that the chrome application tile does not show up in 955 // the running metro apps list on the top left corner. 956 void ChromeAppViewAsh::OnMetroExit(MetroTerminateMethod method) { 957 if (base::win::GetVersion() >= base::win::VERSION_WIN8) { 958 HWND core_window = core_window_hwnd(); 959 if (method == TERMINATE_USING_KEY_SEQUENCE && core_window != NULL && 960 core_window == ::GetForegroundWindow()) { 961 DVLOG(1) << "We are in the foreground. Exiting via Alt F4"; 962 SendKeySequence(VK_F4, ALT); 963 if (ui_channel_) 964 ui_channel_->Close(); 965 } else { 966 globals.app_exit->Exit(); 967 } 968 } else { 969 if (ui_channel_) 970 ui_channel_->Close(); 971 972 globals.app_exit->Exit(); 973 } 974 } 975 976 void ChromeAppViewAsh::OnInputSourceChanged() { 977 if (!input_source_) 978 return; 979 980 LANGID langid = 0; 981 bool is_ime = false; 982 if (!input_source_->GetActiveSource(&langid, &is_ime)) { 983 LOG(ERROR) << "GetActiveSource failed"; 984 return; 985 } 986 ui_channel_->Send(new MetroViewerHostMsg_ImeInputSourceChanged(langid, 987 is_ime)); 988 } 989 990 void ChromeAppViewAsh::OnCompositionChanged( 991 const base::string16& text, 992 int32 selection_start, 993 int32 selection_end, 994 const std::vector<metro_viewer::UnderlineInfo>& underlines) { 995 ui_channel_->Send(new MetroViewerHostMsg_ImeCompositionChanged( 996 text, selection_start, selection_end, underlines)); 997 } 998 999 void ChromeAppViewAsh::OnTextCommitted(const base::string16& text) { 1000 ui_channel_->Send(new MetroViewerHostMsg_ImeTextCommitted(text)); 1001 } 1002 1003 void ChromeAppViewAsh::SendMouseButton(int x, 1004 int y, 1005 int extra, 1006 ui::EventType event_type, 1007 uint32 flags, 1008 ui::EventFlags changed_button, 1009 bool is_horizontal_wheel) { 1010 MetroViewerHostMsg_MouseButtonParams params; 1011 params.x = static_cast<int32>(x); 1012 params.y = static_cast<int32>(y); 1013 params.extra = static_cast<int32>(extra); 1014 params.event_type = event_type; 1015 params.flags = static_cast<int32>(flags); 1016 params.changed_button = changed_button; 1017 params.is_horizontal_wheel = is_horizontal_wheel; 1018 ui_channel_->Send(new MetroViewerHostMsg_MouseButton(params)); 1019 } 1020 1021 void ChromeAppViewAsh::GenerateMouseEventFromMoveIfNecessary( 1022 const PointerInfoHandler& pointer) { 1023 ui::EventType event_type; 1024 // For aura we want the flags to include the button that was released, thus 1025 // we or the old and new. 1026 uint32 mouse_down_flags = pointer.mouse_down_flags() | mouse_down_flags_; 1027 mouse_down_flags_ = pointer.mouse_down_flags(); 1028 switch (pointer.update_kind()) { 1029 case winui::Input::PointerUpdateKind_LeftButtonPressed: 1030 case winui::Input::PointerUpdateKind_RightButtonPressed: 1031 case winui::Input::PointerUpdateKind_MiddleButtonPressed: 1032 event_type = ui::ET_MOUSE_PRESSED; 1033 break; 1034 case winui::Input::PointerUpdateKind_LeftButtonReleased: 1035 case winui::Input::PointerUpdateKind_RightButtonReleased: 1036 case winui::Input::PointerUpdateKind_MiddleButtonReleased: 1037 event_type = ui::ET_MOUSE_RELEASED; 1038 break; 1039 default: 1040 return; 1041 } 1042 SendMouseButton(pointer.x(), pointer.y(), 0, event_type, 1043 mouse_down_flags | GetKeyboardEventFlags(), 1044 pointer.changed_button(), pointer.is_horizontal_wheel()); 1045 } 1046 1047 HRESULT ChromeAppViewAsh::OnActivate( 1048 winapp::Core::ICoreApplicationView*, 1049 winapp::Activation::IActivatedEventArgs* args) { 1050 DVLOG(1) << __FUNCTION__; 1051 // Note: If doing more work in this function, you migth need to call 1052 // get_PreviousExecutionState() and skip the work if the result is 1053 // ApplicationExecutionState_Running and globals.previous_state is too. 1054 args->get_PreviousExecutionState(&globals.previous_state); 1055 DVLOG(1) << "Previous Execution State: " << globals.previous_state; 1056 1057 winapp::Activation::ActivationKind activation_kind; 1058 CheckHR(args->get_Kind(&activation_kind)); 1059 DVLOG(1) << "Activation kind: " << activation_kind; 1060 1061 if (activation_kind == winapp::Activation::ActivationKind_Search) 1062 HandleSearchRequest(args); 1063 else if (activation_kind == winapp::Activation::ActivationKind_Protocol) 1064 HandleProtocolRequest(args); 1065 else 1066 LaunchChromeBrowserProcess(NULL, args); 1067 // We call ICoreWindow::Activate after the handling for the search/protocol 1068 // requests because Chrome can be launched to handle a search request which 1069 // in turn launches the chrome browser process in desktop mode via 1070 // ShellExecute. If we call ICoreWindow::Activate before this, then 1071 // Windows kills the metro chrome process when it calls ShellExecute. Seems 1072 // to be a bug. 1073 window_->Activate(); 1074 return S_OK; 1075 } 1076 1077 HRESULT ChromeAppViewAsh::OnPointerMoved(winui::Core::ICoreWindow* sender, 1078 winui::Core::IPointerEventArgs* args) { 1079 PointerInfoHandler pointer(metro_dpi_scale_, win32_dpi_scale_); 1080 HRESULT hr = pointer.Init(args); 1081 if (FAILED(hr)) 1082 return hr; 1083 1084 if (pointer.IsMouse()) { 1085 GenerateMouseEventFromMoveIfNecessary(pointer); 1086 ui_channel_->Send(new MetroViewerHostMsg_MouseMoved( 1087 pointer.x(), 1088 pointer.y(), 1089 mouse_down_flags_ | GetKeyboardEventFlags())); 1090 } else { 1091 DCHECK(pointer.IsTouch()); 1092 ui_channel_->Send(new MetroViewerHostMsg_TouchMoved(pointer.x(), 1093 pointer.y(), 1094 pointer.timestamp(), 1095 pointer.pointer_id())); 1096 } 1097 return S_OK; 1098 } 1099 1100 // NOTE: From experimentation, it seems like Metro only sends a PointerPressed 1101 // event for the first button pressed and the last button released in a sequence 1102 // of mouse events. 1103 // For example, a sequence of LEFT_DOWN, RIGHT_DOWN, LEFT_UP, RIGHT_UP results 1104 // only in PointerPressed(LEFT)/PointerReleased(RIGHT) events. Intermediary 1105 // presses and releases are tracked in OnPointMoved(). 1106 HRESULT ChromeAppViewAsh::OnPointerPressed( 1107 winui::Core::ICoreWindow* sender, 1108 winui::Core::IPointerEventArgs* args) { 1109 PointerInfoHandler pointer(metro_dpi_scale_, win32_dpi_scale_); 1110 HRESULT hr = pointer.Init(args); 1111 if (FAILED(hr)) 1112 return hr; 1113 1114 if (pointer.IsMouse()) { 1115 mouse_down_flags_ = pointer.mouse_down_flags(); 1116 SendMouseButton(pointer.x(), pointer.y(), 0, ui::ET_MOUSE_PRESSED, 1117 mouse_down_flags_ | GetKeyboardEventFlags(), 1118 pointer.changed_button(), pointer.is_horizontal_wheel()); 1119 } else { 1120 DCHECK(pointer.IsTouch()); 1121 ui_channel_->Send(new MetroViewerHostMsg_TouchDown(pointer.x(), 1122 pointer.y(), 1123 pointer.timestamp(), 1124 pointer.pointer_id())); 1125 } 1126 return S_OK; 1127 } 1128 1129 HRESULT ChromeAppViewAsh::OnPointerReleased( 1130 winui::Core::ICoreWindow* sender, 1131 winui::Core::IPointerEventArgs* args) { 1132 PointerInfoHandler pointer(metro_dpi_scale_, win32_dpi_scale_); 1133 HRESULT hr = pointer.Init(args); 1134 if (FAILED(hr)) 1135 return hr; 1136 1137 if (pointer.IsMouse()) { 1138 mouse_down_flags_ = ui::EF_NONE; 1139 SendMouseButton(pointer.x(), pointer.y(), 0, ui::ET_MOUSE_RELEASED, 1140 static_cast<uint32>(pointer.changed_button()) | 1141 GetKeyboardEventFlags(), 1142 pointer.changed_button(), 1143 pointer.is_horizontal_wheel()); 1144 } else { 1145 DCHECK(pointer.IsTouch()); 1146 ui_channel_->Send(new MetroViewerHostMsg_TouchUp(pointer.x(), 1147 pointer.y(), 1148 pointer.timestamp(), 1149 pointer.pointer_id())); 1150 } 1151 return S_OK; 1152 } 1153 1154 HRESULT ChromeAppViewAsh::OnWheel( 1155 winui::Core::ICoreWindow* sender, 1156 winui::Core::IPointerEventArgs* args) { 1157 PointerInfoHandler pointer(metro_dpi_scale_, win32_dpi_scale_); 1158 HRESULT hr = pointer.Init(args); 1159 if (FAILED(hr)) 1160 return hr; 1161 DCHECK(pointer.IsMouse()); 1162 SendMouseButton(pointer.x(), pointer.y(), pointer.wheel_delta(), 1163 ui::ET_MOUSEWHEEL, ui::EF_NONE, ui::EF_NONE, 1164 pointer.is_horizontal_wheel()); 1165 return S_OK; 1166 } 1167 1168 HRESULT ChromeAppViewAsh::OnKeyDown( 1169 winui::Core::ICoreWindow* sender, 1170 winui::Core::IKeyEventArgs* args) { 1171 winsys::VirtualKey virtual_key; 1172 HRESULT hr = args->get_VirtualKey(&virtual_key); 1173 if (FAILED(hr)) 1174 return hr; 1175 winui::Core::CorePhysicalKeyStatus status; 1176 hr = args->get_KeyStatus(&status); 1177 if (FAILED(hr)) 1178 return hr; 1179 1180 ui_channel_->Send(new MetroViewerHostMsg_KeyDown(virtual_key, 1181 status.RepeatCount, 1182 status.ScanCode, 1183 GetKeyboardEventFlags())); 1184 return S_OK; 1185 } 1186 1187 HRESULT ChromeAppViewAsh::OnKeyUp( 1188 winui::Core::ICoreWindow* sender, 1189 winui::Core::IKeyEventArgs* args) { 1190 winsys::VirtualKey virtual_key; 1191 HRESULT hr = args->get_VirtualKey(&virtual_key); 1192 if (FAILED(hr)) 1193 return hr; 1194 winui::Core::CorePhysicalKeyStatus status; 1195 hr = args->get_KeyStatus(&status); 1196 if (FAILED(hr)) 1197 return hr; 1198 1199 ui_channel_->Send(new MetroViewerHostMsg_KeyUp(virtual_key, 1200 status.RepeatCount, 1201 status.ScanCode, 1202 GetKeyboardEventFlags())); 1203 return S_OK; 1204 } 1205 1206 HRESULT ChromeAppViewAsh::OnAcceleratorKeyDown( 1207 winui::Core::ICoreDispatcher* sender, 1208 winui::Core::IAcceleratorKeyEventArgs* args) { 1209 winsys::VirtualKey virtual_key; 1210 HRESULT hr = args->get_VirtualKey(&virtual_key); 1211 if (FAILED(hr)) 1212 return hr; 1213 winui::Core::CorePhysicalKeyStatus status; 1214 hr = args->get_KeyStatus(&status); 1215 if (FAILED(hr)) 1216 return hr; 1217 1218 winui::Core::CoreAcceleratorKeyEventType event_type; 1219 hr = args->get_EventType(&event_type); 1220 if (FAILED(hr)) 1221 return hr; 1222 1223 uint32 keyboard_flags = GetKeyboardEventFlags(); 1224 1225 switch (event_type) { 1226 case winui::Core::CoreAcceleratorKeyEventType_SystemCharacter: 1227 ui_channel_->Send(new MetroViewerHostMsg_Character(virtual_key, 1228 status.RepeatCount, 1229 status.ScanCode, 1230 keyboard_flags)); 1231 break; 1232 1233 case winui::Core::CoreAcceleratorKeyEventType_SystemKeyDown: 1234 // Don't send the Alt + F4 combination to Chrome as this is intended to 1235 // shut the metro environment down. Reason we check for Control here is 1236 // Windows does not shutdown metro if Ctrl is pressed along with Alt F4. 1237 // Other key combinations with Alt F4 shutdown metro. 1238 if ((virtual_key == VK_F4) && ((keyboard_flags & ui::EF_ALT_DOWN) && 1239 !(keyboard_flags & ui::EF_CONTROL_DOWN))) 1240 return S_OK; 1241 ui_channel_->Send(new MetroViewerHostMsg_KeyDown(virtual_key, 1242 status.RepeatCount, 1243 status.ScanCode, 1244 keyboard_flags)); 1245 break; 1246 1247 case winui::Core::CoreAcceleratorKeyEventType_SystemKeyUp: 1248 ui_channel_->Send(new MetroViewerHostMsg_KeyUp(virtual_key, 1249 status.RepeatCount, 1250 status.ScanCode, 1251 keyboard_flags)); 1252 break; 1253 1254 default: 1255 break; 1256 } 1257 return S_OK; 1258 } 1259 1260 HRESULT ChromeAppViewAsh::OnCharacterReceived( 1261 winui::Core::ICoreWindow* sender, 1262 winui::Core::ICharacterReceivedEventArgs* args) { 1263 unsigned int char_code = 0; 1264 HRESULT hr = args->get_KeyCode(&char_code); 1265 if (FAILED(hr)) 1266 return hr; 1267 1268 winui::Core::CorePhysicalKeyStatus status; 1269 hr = args->get_KeyStatus(&status); 1270 if (FAILED(hr)) 1271 return hr; 1272 1273 ui_channel_->Send(new MetroViewerHostMsg_Character(char_code, 1274 status.RepeatCount, 1275 status.ScanCode, 1276 GetKeyboardEventFlags())); 1277 return S_OK; 1278 } 1279 1280 HRESULT ChromeAppViewAsh::OnWindowActivated( 1281 winui::Core::ICoreWindow* sender, 1282 winui::Core::IWindowActivatedEventArgs* args) { 1283 winui::Core::CoreWindowActivationState state; 1284 HRESULT hr = args->get_WindowActivationState(&state); 1285 if (FAILED(hr)) 1286 return hr; 1287 1288 // Treat both full activation (Ash was reopened from the Start Screen or from 1289 // any other Metro entry point in Windows) and pointer activation (user 1290 // clicked back in Ash after using another app on another monitor) the same. 1291 if (state == winui::Core::CoreWindowActivationState_CodeActivated || 1292 state == winui::Core::CoreWindowActivationState_PointerActivated) { 1293 if (text_service_) 1294 text_service_->OnWindowActivated(); 1295 ui_channel_->Send(new MetroViewerHostMsg_WindowActivated()); 1296 } 1297 return S_OK; 1298 } 1299 1300 HRESULT ChromeAppViewAsh::HandleSearchRequest( 1301 winapp::Activation::IActivatedEventArgs* args) { 1302 mswr::ComPtr<winapp::Activation::ISearchActivatedEventArgs> search_args; 1303 CheckHR(args->QueryInterface( 1304 winapp::Activation::IID_ISearchActivatedEventArgs, &search_args)); 1305 1306 if (!ui_channel_) { 1307 DVLOG(1) << "Launched to handle search request"; 1308 LaunchChromeBrowserProcess(L"--windows8-search", args); 1309 } 1310 1311 mswrw::HString search_string; 1312 CheckHR(search_args->get_QueryText(search_string.GetAddressOf())); 1313 base::string16 search_text(MakeStdWString(search_string.Get())); 1314 1315 ui_loop_.PostTask(FROM_HERE, 1316 base::Bind(&ChromeAppViewAsh::OnSearchRequest, 1317 base::Unretained(this), 1318 search_text)); 1319 return S_OK; 1320 } 1321 1322 HRESULT ChromeAppViewAsh::HandleProtocolRequest( 1323 winapp::Activation::IActivatedEventArgs* args) { 1324 DVLOG(1) << __FUNCTION__; 1325 if (!ui_channel_) 1326 DVLOG(1) << "Launched to handle url request"; 1327 1328 mswr::ComPtr<winapp::Activation::IProtocolActivatedEventArgs> 1329 protocol_args; 1330 CheckHR(args->QueryInterface( 1331 winapp::Activation::IID_IProtocolActivatedEventArgs, 1332 &protocol_args)); 1333 1334 mswr::ComPtr<winfoundtn::IUriRuntimeClass> uri; 1335 protocol_args->get_Uri(&uri); 1336 mswrw::HString url; 1337 uri->get_AbsoluteUri(url.GetAddressOf()); 1338 base::string16 actual_url(MakeStdWString(url.Get())); 1339 DVLOG(1) << "Received url request: " << actual_url; 1340 1341 ui_loop_.PostTask(FROM_HERE, 1342 base::Bind(&ChromeAppViewAsh::OnNavigateToUrl, 1343 base::Unretained(this), 1344 actual_url)); 1345 return S_OK; 1346 } 1347 1348 HRESULT ChromeAppViewAsh::OnEdgeGestureCompleted( 1349 winui::Input::IEdgeGesture* gesture, 1350 winui::Input::IEdgeGestureEventArgs* args) { 1351 ui_channel_->Send(new MetroViewerHostMsg_EdgeGesture()); 1352 return S_OK; 1353 } 1354 1355 void ChromeAppViewAsh::OnSearchRequest(const base::string16& search_string) { 1356 DCHECK(ui_channel_); 1357 ui_channel_->Send(new MetroViewerHostMsg_SearchRequest(search_string)); 1358 } 1359 1360 void ChromeAppViewAsh::OnNavigateToUrl(const base::string16& url) { 1361 DCHECK(ui_channel_); 1362 ui_channel_->Send(new MetroViewerHostMsg_OpenURL(url)); 1363 } 1364 1365 HRESULT ChromeAppViewAsh::OnSizeChanged(winui::Core::ICoreWindow* sender, 1366 winui::Core::IWindowSizeChangedEventArgs* args) { 1367 if (!window_) { 1368 return S_OK; 1369 } 1370 1371 // winui::Core::IWindowSizeChangedEventArgs args->Size appears to return 1372 // scaled values under HiDPI. We will instead use GetWindowRect() which 1373 // should always return values in Pixels. 1374 RECT rect = {0}; 1375 ::GetWindowRect(core_window_hwnd_, &rect); 1376 1377 uint32 cx = static_cast<uint32>(rect.right - rect.left); 1378 uint32 cy = static_cast<uint32>(rect.bottom - rect.top); 1379 1380 DVLOG(1) << "Window size changed: width=" << cx << ", height=" << cy; 1381 ui_channel_->Send(new MetroViewerHostMsg_WindowSizeChanged(cx, cy)); 1382 return S_OK; 1383 } 1384 1385 /////////////////////////////////////////////////////////////////////////////// 1386 1387 ChromeAppViewFactory::ChromeAppViewFactory( 1388 winapp::Core::ICoreApplication* icore_app) { 1389 mswr::ComPtr<winapp::Core::ICoreApplication> core_app(icore_app); 1390 mswr::ComPtr<winapp::Core::ICoreApplicationExit> app_exit; 1391 CheckHR(core_app.As(&app_exit)); 1392 globals.app_exit = app_exit.Detach(); 1393 } 1394 1395 IFACEMETHODIMP 1396 ChromeAppViewFactory::CreateView(winapp::Core::IFrameworkView** view) { 1397 *view = mswr::Make<ChromeAppViewAsh>().Detach(); 1398 return (*view) ? S_OK : E_OUTOFMEMORY; 1399 } 1400