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 |