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