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