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
36typedef winfoundtn::ITypedEventHandler<
37    winapp::Core::CoreApplicationView*,
38    winapp::Activation::IActivatedEventArgs*> ActivatedHandler;
39
40typedef winfoundtn::ITypedEventHandler<
41    winui::Core::CoreWindow*,
42    winui::Core::PointerEventArgs*> PointerEventHandler;
43
44typedef winfoundtn::ITypedEventHandler<
45    winui::Core::CoreWindow*,
46    winui::Core::KeyEventArgs*> KeyEventHandler;
47
48typedef winfoundtn::ITypedEventHandler<
49    winui::Core::CoreDispatcher*,
50    winui::Core::AcceleratorKeyEventArgs*> AcceleratorKeyEventHandler;
51
52typedef winfoundtn::ITypedEventHandler<
53    winui::Core::CoreWindow*,
54    winui::Core::CharacterReceivedEventArgs*> CharEventHandler;
55
56typedef winfoundtn::ITypedEventHandler<
57    winui::Core::CoreWindow*,
58    winui::Core::WindowActivatedEventArgs*> WindowActivatedHandler;
59
60typedef winfoundtn::ITypedEventHandler<
61    winui::Core::CoreWindow*,
62    winui::Core::WindowSizeChangedEventArgs*> SizeChangedHandler;
63
64typedef winfoundtn::ITypedEventHandler<
65    winui::Input::EdgeGesture*,
66    winui::Input::EdgeGestureEventArgs*> EdgeEventHandler;
67
68// This function is exported by chrome.exe.
69typedef int (__cdecl *BreakpadExceptionHandler)(EXCEPTION_POINTERS* info);
70
71// Global information used across the metro driver.
72struct Globals {
73  winapp::Activation::ApplicationExecutionState previous_state;
74  winapp::Core::ICoreApplicationExit* app_exit;
75  BreakpadExceptionHandler breakpad_exception_handler;
76} globals;
77
78extern float GetModernUIScale();
79
80namespace {
81
82enum KeyModifier {
83  NONE,
84  SHIFT = 1,
85  CONTROL = 2,
86  ALT = 4
87};
88
89const 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.
94void 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
143class 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
277void 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.
296uint32 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
307bool 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.
356class 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
519ChromeAppViewAsh::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
532ChromeAppViewAsh::~ChromeAppViewAsh() {
533  DVLOG(1) << __FUNCTION__;
534}
535
536IFACEMETHODIMP
537ChromeAppViewAsh::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
547IFACEMETHODIMP
548ChromeAppViewAsh::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
658IFACEMETHODIMP
659ChromeAppViewAsh::Load(HSTRING entryPoint) {
660  // On Win7 |entryPoint| is NULL.
661  DVLOG(1) << __FUNCTION__;
662  return S_OK;
663}
664
665IFACEMETHODIMP
666ChromeAppViewAsh::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
700IFACEMETHODIMP
701ChromeAppViewAsh::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
713HRESULT 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
738void 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
769void 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
781void ChromeAppViewAsh::OnSetCursor(HCURSOR cursor) {
782  ::SetCursor(cursor);
783  last_cursor_ = cursor;
784}
785
786void 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
805void 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
817void 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
826void 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
839void 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
856void 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
870void 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
883void ChromeAppViewAsh::OnImeCancelComposition() {
884  if (!text_service_)
885    return;
886  text_service_->CancelComposition();
887}
888
889void 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
897void 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.
921void 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
944void 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
960void 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
969void ChromeAppViewAsh::OnTextCommitted(const base::string16& text) {
970  ui_channel_->Send(new MetroViewerHostMsg_ImeTextCommitted(text));
971}
972
973void 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
993void 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
1019HRESULT 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
1049HRESULT 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().
1086HRESULT 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
1112HRESULT 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
1140HRESULT 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
1157HRESULT 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
1179HRESULT 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
1201HRESULT 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      ui_channel_->Send(new MetroViewerHostMsg_KeyDown(virtual_key,
1240                                                       status.RepeatCount,
1241                                                       status.ScanCode,
1242                                                       keyboard_flags));
1243      break;
1244
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;
1251
1252    default:
1253      break;
1254  }
1255  return S_OK;
1256}
1257
1258HRESULT ChromeAppViewAsh::OnCharacterReceived(
1259  winui::Core::ICoreWindow* sender,
1260  winui::Core::ICharacterReceivedEventArgs* args) {
1261  if (!ui_channel_)
1262    return S_OK;
1263
1264  unsigned int char_code = 0;
1265  HRESULT hr = args->get_KeyCode(&char_code);
1266  if (FAILED(hr))
1267    return hr;
1268
1269  winui::Core::CorePhysicalKeyStatus status;
1270  hr = args->get_KeyStatus(&status);
1271  if (FAILED(hr))
1272    return hr;
1273
1274  ui_channel_->Send(new MetroViewerHostMsg_Character(char_code,
1275                                                     status.RepeatCount,
1276                                                     status.ScanCode,
1277                                                     GetKeyboardEventFlags()));
1278  return S_OK;
1279}
1280
1281HRESULT ChromeAppViewAsh::OnWindowActivated(
1282    winui::Core::ICoreWindow* sender,
1283    winui::Core::IWindowActivatedEventArgs* args) {
1284  if (!ui_channel_)
1285    return S_OK;
1286
1287  if (args) {
1288    winui::Core::CoreWindowActivationState state;
1289    HRESULT hr = args->get_WindowActivationState(&state);
1290    if (FAILED(hr))
1291      return hr;
1292
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));
1302    }
1303  } else {
1304    // On Windows 7, we force a repaint when the window is activated.
1305    ui_channel_->Send(new MetroViewerHostMsg_WindowActivated(true));
1306  }
1307  return S_OK;
1308}
1309
1310HRESULT 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));
1315
1316  if (!ui_channel_) {
1317    DVLOG(1) << "Launched to handle search request";
1318    LaunchChromeBrowserProcess(L"--windows8-search", args);
1319  }
1320
1321  mswrw::HString search_string;
1322  CheckHR(search_args->get_QueryText(search_string.GetAddressOf()));
1323  base::string16 search_text(MakeStdWString(search_string.Get()));
1324
1325  ui_loop_.PostTask(FROM_HERE,
1326                    base::Bind(&ChromeAppViewAsh::OnSearchRequest,
1327                    base::Unretained(this),
1328                    search_text));
1329  return S_OK;
1330}
1331
1332HRESULT ChromeAppViewAsh::HandleProtocolRequest(
1333    winapp::Activation::IActivatedEventArgs* args) {
1334  DVLOG(1) << __FUNCTION__;
1335  if (!ui_channel_)
1336    DVLOG(1) << "Launched to handle url request";
1337
1338  mswr::ComPtr<winapp::Activation::IProtocolActivatedEventArgs>
1339      protocol_args;
1340  CheckHR(args->QueryInterface(
1341          winapp::Activation::IID_IProtocolActivatedEventArgs,
1342          &protocol_args));
1343
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;
1350
1351  ui_loop_.PostTask(FROM_HERE,
1352                    base::Bind(&ChromeAppViewAsh::OnNavigateToUrl,
1353                               base::Unretained(this),
1354                               actual_url));
1355  return S_OK;
1356}
1357
1358HRESULT 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;
1364}
1365
1366void ChromeAppViewAsh::OnSearchRequest(const base::string16& search_string) {
1367  if (ui_channel_)
1368    ui_channel_->Send(new MetroViewerHostMsg_SearchRequest(search_string));
1369}
1370
1371void ChromeAppViewAsh::OnNavigateToUrl(const base::string16& url) {
1372  if (ui_channel_)
1373    ui_channel_->Send(new MetroViewerHostMsg_OpenURL(url));
1374}
1375
1376HRESULT ChromeAppViewAsh::OnSizeChanged(winui::Core::ICoreWindow* sender,
1377    winui::Core::IWindowSizeChangedEventArgs* args) {
1378  if (!window_) {
1379    return S_OK;
1380  }
1381
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);
1387
1388  uint32 cx = static_cast<uint32>(rect.right - rect.left);
1389  uint32 cy = static_cast<uint32>(rect.bottom - rect.top);
1390
1391  DVLOG(1) << "Window size changed: width=" << cx << ", height=" << cy;
1392  ui_channel_->Send(new MetroViewerHostMsg_WindowSizeChanged(cx, cy));
1393  return S_OK;
1394}
1395
1396void ChromeAppViewAsh::StartChromeOSMode() {
1397  static int ms_elapsed = 0;
1398
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;
1407  }
1408
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;
1416  }
1417
1418  DVLOG(1) << "Found channel : " << win8::kMetroViewerIPCChannelName;
1419
1420  DCHECK(channel_listener_);
1421
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";
1430
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_;
1437
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));
1445
1446  input_source_ = metro_driver::InputSource::Create();
1447  if (input_source_) {
1448    input_source_->AddObserver(this);
1449    // Send an initial input source.
1450    OnInputSourceChanged();
1451  }
1452
1453  // Start receiving IME popup window notifications.
1454  metro_driver::AddImePopupObserver(this);
1455
1456  DVLOG(1) << "Channel setup complete";
1457}
1458
1459///////////////////////////////////////////////////////////////////////////////
1460
1461ChromeAppViewFactory::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();
1467}
1468
1469IFACEMETHODIMP
1470ChromeAppViewFactory::CreateView(winapp::Core::IFrameworkView** view) {
1471  *view = mswr::Make<ChromeAppViewAsh>().Detach();
1472  return (*view) ? S_OK :  E_OUTOFMEMORY;
1473}
1474