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