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