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