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