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/message_loop/message_loop.h"
     15 #include "base/path_service.h"
     16 #include "base/threading/thread.h"
     17 #include "base/win/metro.h"
     18 #include "base/win/win_util.h"
     19 #include "chrome/common/chrome_switches.h"
     20 #include "ipc/ipc_channel.h"
     21 #include "ipc/ipc_channel_proxy.h"
     22 #include "ipc/ipc_sender.h"
     23 #include "ui/base/gestures/gesture_sequence.h"
     24 #include "ui/metro_viewer/metro_viewer_messages.h"
     25 #include "win8/metro_driver/file_picker_ash.h"
     26 #include "win8/metro_driver/metro_driver.h"
     27 #include "win8/metro_driver/winrt_utils.h"
     28 #include "win8/viewer/metro_viewer_constants.h"
     29 
     30 typedef winfoundtn::ITypedEventHandler<
     31     winapp::Core::CoreApplicationView*,
     32     winapp::Activation::IActivatedEventArgs*> ActivatedHandler;
     33 
     34 typedef winfoundtn::ITypedEventHandler<
     35     winui::Core::CoreWindow*,
     36     winui::Core::PointerEventArgs*> PointerEventHandler;
     37 
     38 typedef winfoundtn::ITypedEventHandler<
     39     winui::Core::CoreWindow*,
     40     winui::Core::KeyEventArgs*> KeyEventHandler;
     41 
     42 typedef winfoundtn::ITypedEventHandler<
     43     winui::Core::CoreDispatcher*,
     44     winui::Core::AcceleratorKeyEventArgs*> AcceleratorKeyEventHandler;
     45 
     46 typedef winfoundtn::ITypedEventHandler<
     47     winui::Core::CoreWindow*,
     48     winui::Core::CharacterReceivedEventArgs*> CharEventHandler;
     49 
     50 typedef winfoundtn::ITypedEventHandler<
     51     winui::Core::CoreWindow*,
     52     winui::Core::VisibilityChangedEventArgs*> VisibilityChangedHandler;
     53 
     54 typedef winfoundtn::ITypedEventHandler<
     55     winui::Core::CoreWindow*,
     56     winui::Core::WindowActivatedEventArgs*> WindowActivatedHandler;
     57 
     58 typedef winfoundtn::ITypedEventHandler<
     59     winui::Core::CoreWindow*,
     60     winui::Core::WindowSizeChangedEventArgs*> SizeChangedHandler;
     61 
     62 // This function is exported by chrome.exe.
     63 typedef int (__cdecl *BreakpadExceptionHandler)(EXCEPTION_POINTERS* info);
     64 
     65 // Global information used across the metro driver.
     66 struct Globals {
     67   winapp::Activation::ApplicationExecutionState previous_state;
     68   winapp::Core::ICoreApplicationExit* app_exit;
     69   BreakpadExceptionHandler breakpad_exception_handler;
     70 } globals;
     71 
     72 namespace {
     73 
     74 // TODO(robertshield): Share this with chrome_app_view.cc
     75 void MetroExit() {
     76   globals.app_exit->Exit();
     77 }
     78 
     79 class ChromeChannelListener : public IPC::Listener {
     80  public:
     81   ChromeChannelListener(base::MessageLoop* ui_loop, ChromeAppViewAsh* app_view)
     82       : ui_proxy_(ui_loop->message_loop_proxy()), app_view_(app_view) {}
     83 
     84   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
     85     IPC_BEGIN_MESSAGE_MAP(ChromeChannelListener, message)
     86       IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SetCursor, OnSetCursor)
     87       IPC_MESSAGE_HANDLER(MetroViewerHostMsg_DisplayFileOpen,
     88                           OnDisplayFileOpenDialog)
     89       IPC_MESSAGE_HANDLER(MetroViewerHostMsg_DisplayFileSaveAs,
     90                           OnDisplayFileSaveAsDialog)
     91       IPC_MESSAGE_HANDLER(MetroViewerHostMsg_DisplaySelectFolder,
     92                           OnDisplayFolderPicker)
     93       IPC_MESSAGE_HANDLER(MetroViewerHostMsg_SetCursorPos, OnSetCursorPos)
     94       IPC_MESSAGE_UNHANDLED(__debugbreak())
     95     IPC_END_MESSAGE_MAP()
     96     return true;
     97   }
     98 
     99   virtual void OnChannelError() OVERRIDE {
    100     DVLOG(1) << "Channel error";
    101     MetroExit();
    102   }
    103 
    104  private:
    105   void OnSetCursor(int64 cursor) {
    106     ui_proxy_->PostTask(FROM_HERE,
    107                         base::Bind(&ChromeAppViewAsh::OnSetCursor,
    108                                    base::Unretained(app_view_),
    109                                    reinterpret_cast<HCURSOR>(cursor)));
    110   }
    111 
    112   void OnDisplayFileOpenDialog(const string16& title,
    113                                const string16& filter,
    114                                const base::FilePath& default_path,
    115                                bool allow_multiple_files) {
    116     ui_proxy_->PostTask(FROM_HERE,
    117                         base::Bind(&ChromeAppViewAsh::OnDisplayFileOpenDialog,
    118                                    base::Unretained(app_view_),
    119                                    title,
    120                                    filter,
    121                                    default_path,
    122                                    allow_multiple_files));
    123   }
    124 
    125   void OnDisplayFileSaveAsDialog(
    126     const MetroViewerHostMsg_SaveAsDialogParams& params) {
    127     ui_proxy_->PostTask(
    128         FROM_HERE,
    129         base::Bind(&ChromeAppViewAsh::OnDisplayFileSaveAsDialog,
    130                    base::Unretained(app_view_),
    131                    params));
    132   }
    133 
    134   void OnDisplayFolderPicker(const string16& title) {
    135     ui_proxy_->PostTask(
    136         FROM_HERE,
    137         base::Bind(&ChromeAppViewAsh::OnDisplayFolderPicker,
    138                    base::Unretained(app_view_),
    139                    title));
    140   }
    141 
    142   void OnSetCursorPos(int x, int y) {
    143     VLOG(1) << "In IPC OnSetCursorPos: " << x << ", " << y;
    144     ui_proxy_->PostTask(
    145         FROM_HERE,
    146         base::Bind(&ChromeAppViewAsh::OnSetCursorPos,
    147                    base::Unretained(app_view_),
    148                    x, y));
    149   }
    150 
    151 
    152   scoped_refptr<base::MessageLoopProxy> ui_proxy_;
    153   ChromeAppViewAsh* app_view_;
    154 };
    155 
    156 bool WaitForChromeIPCConnection(const std::string& channel_name) {
    157   int ms_elapsed = 0;
    158   while (!IPC::Channel::IsNamedServerInitialized(channel_name) &&
    159          ms_elapsed < 10000) {
    160     ms_elapsed += 500;
    161     Sleep(500);
    162   }
    163   return IPC::Channel::IsNamedServerInitialized(channel_name);
    164 }
    165 
    166 // This class helps decoding the pointer properties of an event.
    167 class PointerInfoHandler {
    168  public:
    169   PointerInfoHandler() :
    170       x_(0),
    171       y_(0),
    172       wheel_delta_(0),
    173       update_kind_(winui::Input::PointerUpdateKind_Other),
    174       timestamp_(0),
    175       pointer_id_(0) {}
    176 
    177   HRESULT Init(winui::Core::IPointerEventArgs* args) {
    178     HRESULT hr = args->get_CurrentPoint(&pointer_point_);
    179     if (FAILED(hr))
    180       return hr;
    181 
    182     winfoundtn::Point point;
    183     hr = pointer_point_->get_Position(&point);
    184     if (FAILED(hr))
    185       return hr;
    186 
    187     mswr::ComPtr<winui::Input::IPointerPointProperties> properties;
    188     hr = pointer_point_->get_Properties(&properties);
    189     if (FAILED(hr))
    190       return hr;
    191 
    192     hr = properties->get_PointerUpdateKind(&update_kind_);
    193     if (FAILED(hr))
    194       return hr;
    195 
    196     hr = properties->get_MouseWheelDelta(&wheel_delta_);
    197     if (FAILED(hr))
    198       return hr;
    199     x_ = point.X;
    200     y_ = point.Y;
    201     pointer_point_->get_Timestamp(&timestamp_);
    202     pointer_point_->get_PointerId(&pointer_id_);
    203     // Map the OS touch event id to a range allowed by the gesture recognizer.
    204     if (IsTouch())
    205       pointer_id_ %= ui::GestureSequence::kMaxGesturePoints;
    206     return S_OK;
    207   }
    208 
    209   bool IsType(windevs::Input::PointerDeviceType type) const {
    210     mswr::ComPtr<windevs::Input::IPointerDevice> pointer_device;
    211     CheckHR(pointer_point_->get_PointerDevice(&pointer_device));
    212     windevs::Input::PointerDeviceType device_type;
    213     CheckHR(pointer_device->get_PointerDeviceType(&device_type));
    214     return  (device_type == type);
    215   }
    216 
    217   bool IsMouse() const {
    218     return IsType(windevs::Input::PointerDeviceType_Mouse);
    219   }
    220 
    221   bool IsTouch() const {
    222     return IsType(windevs::Input::PointerDeviceType_Touch);
    223   }
    224 
    225   int32 wheel_delta() const {
    226     return wheel_delta_;
    227   }
    228 
    229   ui::EventFlags flags() {
    230     switch (update_kind_) {
    231       case winui::Input::PointerUpdateKind_LeftButtonPressed:
    232         return ui::EF_LEFT_MOUSE_BUTTON;
    233       case winui::Input::PointerUpdateKind_LeftButtonReleased:
    234         return ui::EF_LEFT_MOUSE_BUTTON;
    235       case winui::Input::PointerUpdateKind_RightButtonPressed:
    236         return ui::EF_RIGHT_MOUSE_BUTTON;
    237       case winui::Input::PointerUpdateKind_RightButtonReleased:
    238         return ui::EF_RIGHT_MOUSE_BUTTON;
    239       case winui::Input::PointerUpdateKind_MiddleButtonPressed:
    240         return ui::EF_MIDDLE_MOUSE_BUTTON;
    241       case winui::Input::PointerUpdateKind_MiddleButtonReleased:
    242         return ui::EF_MIDDLE_MOUSE_BUTTON;
    243       default:
    244         return ui::EF_NONE;
    245     };
    246   }
    247 
    248   int x() const { return x_; }
    249   int y() const { return y_; }
    250 
    251   uint32 pointer_id() const {
    252     return pointer_id_;
    253   }
    254 
    255   uint64 timestamp() const { return timestamp_; }
    256 
    257  private:
    258   int x_;
    259   int y_;
    260   int wheel_delta_;
    261   uint32 pointer_id_;
    262   winui::Input::PointerUpdateKind update_kind_;
    263   mswr::ComPtr<winui::Input::IPointerPoint> pointer_point_;
    264   uint64 timestamp_;
    265 };
    266 
    267 void RunMessageLoop(winui::Core::ICoreDispatcher* dispatcher) {
    268   // We're entering a nested message loop, let's allow dispatching
    269   // tasks while we're in there.
    270   base::MessageLoop::current()->SetNestableTasksAllowed(true);
    271 
    272   // Enter main core message loop. There are several ways to exit it
    273   // Nicely:
    274   // 1 - User action like ALT-F4.
    275   // 2 - Calling ICoreApplicationExit::Exit().
    276   // 3-  Posting WM_CLOSE to the core window.
    277   HRESULT hr = dispatcher->ProcessEvents(
    278       winui::Core::CoreProcessEventsOption
    279           ::CoreProcessEventsOption_ProcessUntilQuit);
    280 
    281   // Wind down the thread's chrome message loop.
    282   base::MessageLoop::current()->Quit();
    283 }
    284 
    285 // Helper to return the state of the shift/control/alt keys.
    286 uint32 GetKeyboardEventFlags() {
    287   uint32 flags = 0;
    288   if (base::win::IsShiftPressed())
    289     flags |= ui::EF_SHIFT_DOWN;
    290   if (base::win::IsCtrlPressed())
    291     flags |= ui::EF_CONTROL_DOWN;
    292   if (base::win::IsAltPressed())
    293     flags |= ui::EF_ALT_DOWN;
    294   return flags;
    295 }
    296 
    297 }  // namespace
    298 
    299 ChromeAppViewAsh::ChromeAppViewAsh()
    300     : mouse_down_flags_(ui::EF_NONE),
    301       ui_channel_(nullptr),
    302       core_window_hwnd_(NULL),
    303       ui_loop_(base::MessageLoop::TYPE_UI) {
    304   DVLOG(1) << __FUNCTION__;
    305   globals.previous_state =
    306       winapp::Activation::ApplicationExecutionState_NotRunning;
    307 }
    308 
    309 ChromeAppViewAsh::~ChromeAppViewAsh() {
    310   DVLOG(1) << __FUNCTION__;
    311 }
    312 
    313 IFACEMETHODIMP
    314 ChromeAppViewAsh::Initialize(winapp::Core::ICoreApplicationView* view) {
    315   view_ = view;
    316   DVLOG(1) << __FUNCTION__;
    317   HRESULT hr = view_->add_Activated(mswr::Callback<ActivatedHandler>(
    318       this, &ChromeAppViewAsh::OnActivate).Get(),
    319       &activated_token_);
    320   CheckHR(hr);
    321   return hr;
    322 }
    323 
    324 IFACEMETHODIMP
    325 ChromeAppViewAsh::SetWindow(winui::Core::ICoreWindow* window) {
    326   window_ = window;
    327   DVLOG(1) << __FUNCTION__;
    328 
    329   // Retrieve the native window handle via the interop layer.
    330   mswr::ComPtr<ICoreWindowInterop> interop;
    331   HRESULT hr = window->QueryInterface(interop.GetAddressOf());
    332   CheckHR(hr);
    333   hr = interop->get_WindowHandle(&core_window_hwnd_);
    334   CheckHR(hr);
    335 
    336   hr = window_->add_SizeChanged(mswr::Callback<SizeChangedHandler>(
    337       this, &ChromeAppViewAsh::OnSizeChanged).Get(),
    338       &sizechange_token_);
    339   CheckHR(hr);
    340 
    341   // Register for pointer and keyboard notifications. We forward
    342   // them to the browser process via IPC.
    343   hr = window_->add_PointerMoved(mswr::Callback<PointerEventHandler>(
    344       this, &ChromeAppViewAsh::OnPointerMoved).Get(),
    345       &pointermoved_token_);
    346   CheckHR(hr);
    347 
    348   hr = window_->add_PointerPressed(mswr::Callback<PointerEventHandler>(
    349       this, &ChromeAppViewAsh::OnPointerPressed).Get(),
    350       &pointerpressed_token_);
    351   CheckHR(hr);
    352 
    353   hr = window_->add_PointerReleased(mswr::Callback<PointerEventHandler>(
    354       this, &ChromeAppViewAsh::OnPointerReleased).Get(),
    355       &pointerreleased_token_);
    356   CheckHR(hr);
    357 
    358   hr = window_->add_KeyDown(mswr::Callback<KeyEventHandler>(
    359       this, &ChromeAppViewAsh::OnKeyDown).Get(),
    360       &keydown_token_);
    361   CheckHR(hr);
    362 
    363   hr = window_->add_KeyUp(mswr::Callback<KeyEventHandler>(
    364       this, &ChromeAppViewAsh::OnKeyUp).Get(),
    365       &keyup_token_);
    366   CheckHR(hr);
    367 
    368   mswr::ComPtr<winui::Core::ICoreDispatcher> dispatcher;
    369   hr = window_->get_Dispatcher(&dispatcher);
    370   CheckHR(hr, "Get Dispatcher failed.");
    371 
    372   mswr::ComPtr<winui::Core::ICoreAcceleratorKeys> accelerator_keys;
    373   hr = dispatcher.CopyTo(__uuidof(winui::Core::ICoreAcceleratorKeys),
    374                          reinterpret_cast<void**>(
    375                             accelerator_keys.GetAddressOf()));
    376   CheckHR(hr, "QI for ICoreAcceleratorKeys failed.");
    377   hr = accelerator_keys->add_AcceleratorKeyActivated(
    378       mswr::Callback<AcceleratorKeyEventHandler>(
    379           this, &ChromeAppViewAsh::OnAcceleratorKeyDown).Get(),
    380       &accel_keydown_token_);
    381   CheckHR(hr);
    382 
    383   hr = window_->add_PointerWheelChanged(mswr::Callback<PointerEventHandler>(
    384       this, &ChromeAppViewAsh::OnWheel).Get(),
    385       &wheel_token_);
    386   CheckHR(hr);
    387 
    388   hr = window_->add_CharacterReceived(mswr::Callback<CharEventHandler>(
    389       this, &ChromeAppViewAsh::OnCharacterReceived).Get(),
    390       &character_received_token_);
    391   CheckHR(hr);
    392 
    393   hr = window_->add_VisibilityChanged(mswr::Callback<VisibilityChangedHandler>(
    394       this, &ChromeAppViewAsh::OnVisibilityChanged).Get(),
    395       &visibility_changed_token_);
    396   CheckHR(hr);
    397 
    398   hr = window_->add_Activated(mswr::Callback<WindowActivatedHandler>(
    399       this, &ChromeAppViewAsh::OnWindowActivated).Get(),
    400       &window_activated_token_);
    401   CheckHR(hr);
    402 
    403   // By initializing the direct 3D swap chain with the corewindow
    404   // we can now directly blit to it from the browser process.
    405   direct3d_helper_.Initialize(window);
    406   DVLOG(1) << "Initialized Direct3D.";
    407   return S_OK;
    408 }
    409 
    410 IFACEMETHODIMP
    411 ChromeAppViewAsh::Load(HSTRING entryPoint) {
    412   DVLOG(1) << __FUNCTION__;
    413   return S_OK;
    414 }
    415 
    416 IFACEMETHODIMP
    417 ChromeAppViewAsh::Run() {
    418   DVLOG(1) << __FUNCTION__;
    419   mswr::ComPtr<winui::Core::ICoreDispatcher> dispatcher;
    420   HRESULT hr = window_->get_Dispatcher(&dispatcher);
    421   CheckHR(hr, "Dispatcher failed.");
    422 
    423   hr = window_->Activate();
    424   if (FAILED(hr)) {
    425     DLOG(WARNING) << "activation failed hr=" << hr;
    426     return hr;
    427   }
    428 
    429   // Create the IPC channel IO thread. It needs to out-live the ChannelProxy.
    430   base::Thread io_thread("metro_IO_thread");
    431   base::Thread::Options options;
    432   options.message_loop_type = base::MessageLoop::TYPE_IO;
    433   io_thread.StartWithOptions(options);
    434 
    435   // Start up Chrome and wait for the desired IPC server connection to exist.
    436   WaitForChromeIPCConnection(win8::kMetroViewerIPCChannelName);
    437 
    438   // In Aura mode we create an IPC channel to the browser, then ask it to
    439   // connect to us.
    440   ChromeChannelListener ui_channel_listener(&ui_loop_, this);
    441   IPC::ChannelProxy ui_channel(win8::kMetroViewerIPCChannelName,
    442                                IPC::Channel::MODE_NAMED_CLIENT,
    443                                &ui_channel_listener,
    444                                io_thread.message_loop_proxy());
    445   ui_channel_ = &ui_channel;
    446 
    447   // Upon receipt of the MetroViewerHostMsg_SetTargetSurface message the
    448   // browser will use D3D from the browser process to present to our Window.
    449   ui_channel_->Send(new MetroViewerHostMsg_SetTargetSurface(
    450                     gfx::NativeViewId(core_window_hwnd_)));
    451   DVLOG(1) << "ICoreWindow sent " << core_window_hwnd_;
    452 
    453   // Send an initial size message so that the Ash root window host gets sized
    454   // correctly.
    455   RECT rect = {0};
    456   ::GetWindowRect(core_window_hwnd_, &rect);
    457   ui_channel_->Send(
    458       new MetroViewerHostMsg_WindowSizeChanged(rect.right - rect.left,
    459                                                rect.bottom - rect.top));
    460 
    461   // And post the task that'll do the inner Metro message pumping to it.
    462   ui_loop_.PostTask(FROM_HERE, base::Bind(&RunMessageLoop, dispatcher.Get()));
    463   ui_loop_.Run();
    464 
    465   DVLOG(0) << "ProcessEvents done, hr=" << hr;
    466   return hr;
    467 }
    468 
    469 IFACEMETHODIMP
    470 ChromeAppViewAsh::Uninitialize() {
    471   DVLOG(1) << __FUNCTION__;
    472   window_ = nullptr;
    473   view_ = nullptr;
    474   core_window_hwnd_ = NULL;
    475   return S_OK;
    476 }
    477 
    478 // static
    479 HRESULT ChromeAppViewAsh::Unsnap() {
    480   mswr::ComPtr<winui::ViewManagement::IApplicationViewStatics> view_statics;
    481   HRESULT hr = winrt_utils::CreateActivationFactory(
    482       RuntimeClass_Windows_UI_ViewManagement_ApplicationView,
    483       view_statics.GetAddressOf());
    484   CheckHR(hr);
    485 
    486   winui::ViewManagement::ApplicationViewState state =
    487       winui::ViewManagement::ApplicationViewState_FullScreenLandscape;
    488   hr = view_statics->get_Value(&state);
    489   CheckHR(hr);
    490 
    491   if (state == winui::ViewManagement::ApplicationViewState_Snapped) {
    492     boolean success = FALSE;
    493     hr = view_statics->TryUnsnap(&success);
    494 
    495     if (FAILED(hr) || !success) {
    496       LOG(ERROR) << "Failed to unsnap. Error 0x" << hr;
    497       if (SUCCEEDED(hr))
    498         hr = E_UNEXPECTED;
    499     }
    500   }
    501   return hr;
    502 }
    503 
    504 
    505 void ChromeAppViewAsh::OnSetCursor(HCURSOR cursor) {
    506   ::SetCursor(HCURSOR(cursor));
    507 }
    508 
    509 void ChromeAppViewAsh::OnDisplayFileOpenDialog(
    510     const string16& title,
    511     const string16& filter,
    512     const base::FilePath& default_path,
    513     bool allow_multiple_files) {
    514   DVLOG(1) << __FUNCTION__;
    515 
    516   // The OpenFilePickerSession instance is deleted when we receive a
    517   // callback from the OpenFilePickerSession class about the completion of the
    518   // operation.
    519   FilePickerSessionBase* file_picker_ =
    520       new OpenFilePickerSession(this,
    521                                 title,
    522                                 filter,
    523                                 default_path,
    524                                 allow_multiple_files);
    525   file_picker_->Run();
    526 }
    527 
    528 void ChromeAppViewAsh::OnDisplayFileSaveAsDialog(
    529     const MetroViewerHostMsg_SaveAsDialogParams& params) {
    530   DVLOG(1) << __FUNCTION__;
    531 
    532   // The SaveFilePickerSession instance is deleted when we receive a
    533   // callback from the SaveFilePickerSession class about the completion of the
    534   // operation.
    535   FilePickerSessionBase* file_picker_ =
    536       new SaveFilePickerSession(this, params);
    537   file_picker_->Run();
    538 }
    539 
    540 void ChromeAppViewAsh::OnDisplayFolderPicker(const string16& title) {
    541   DVLOG(1) << __FUNCTION__;
    542   // The FolderPickerSession instance is deleted when we receive a
    543   // callback from the FolderPickerSession class about the completion of the
    544   // operation.
    545   FilePickerSessionBase* file_picker_ = new FolderPickerSession(this, title);
    546   file_picker_->Run();
    547 }
    548 
    549 void ChromeAppViewAsh::OnSetCursorPos(int x, int y) {
    550   if (ui_channel_) {
    551     ::SetCursorPos(x, y);
    552     DVLOG(1) << "In UI OnSetCursorPos: " << x << ", " << y;
    553     ui_channel_->Send(new MetroViewerHostMsg_SetCursorPosAck());
    554     // Generate a fake mouse move which matches the SetCursor coordinates as
    555     // the browser expects to receive a mouse move for these coordinates.
    556     // It is not clear why we don't receive a real mouse move in response to
    557     // the SetCursorPos calll above.
    558     ui_channel_->Send(new MetroViewerHostMsg_MouseMoved(x, y, 0));
    559   }
    560 }
    561 
    562 void ChromeAppViewAsh::OnOpenFileCompleted(
    563     OpenFilePickerSession* open_file_picker,
    564     bool success) {
    565   DVLOG(1) << __FUNCTION__;
    566   DVLOG(1) << "Success: " << success;
    567   if (ui_channel_) {
    568     if (open_file_picker->allow_multi_select()) {
    569       ui_channel_->Send(new MetroViewerHostMsg_MultiFileOpenDone(
    570           success, open_file_picker->filenames()));
    571     } else {
    572       ui_channel_->Send(new MetroViewerHostMsg_FileOpenDone(
    573           success, base::FilePath(open_file_picker->result())));
    574     }
    575   }
    576   delete open_file_picker;
    577 }
    578 
    579 void ChromeAppViewAsh::OnSaveFileCompleted(
    580     SaveFilePickerSession* save_file_picker,
    581     bool success) {
    582   DVLOG(1) << __FUNCTION__;
    583   DVLOG(1) << "Success: " << success;
    584   if (ui_channel_) {
    585     ui_channel_->Send(new MetroViewerHostMsg_FileSaveAsDone(
    586         success,
    587         base::FilePath(save_file_picker->result()),
    588         save_file_picker->filter_index()));
    589   }
    590   delete save_file_picker;
    591 }
    592 
    593 void ChromeAppViewAsh::OnFolderPickerCompleted(
    594     FolderPickerSession* folder_picker,
    595     bool success) {
    596   DVLOG(1) << __FUNCTION__;
    597   DVLOG(1) << "Success: " << success;
    598   if (ui_channel_) {
    599     ui_channel_->Send(new MetroViewerHostMsg_SelectFolderDone(
    600         success,
    601         base::FilePath(folder_picker->result())));
    602   }
    603   delete folder_picker;
    604 }
    605 
    606 HRESULT ChromeAppViewAsh::OnActivate(
    607     winapp::Core::ICoreApplicationView*,
    608     winapp::Activation::IActivatedEventArgs* args) {
    609   DVLOG(1) << __FUNCTION__;
    610   // Note: If doing more work in this function, you migth need to call
    611   // get_PreviousExecutionState() and skip the work if  the result is
    612   // ApplicationExecutionState_Running and globals.previous_state is too.
    613   args->get_PreviousExecutionState(&globals.previous_state);
    614   DVLOG(1) << "Previous Execution State: " << globals.previous_state;
    615 
    616   winapp::Activation::ActivationKind activation_kind;
    617   CheckHR(args->get_Kind(&activation_kind));
    618   if (activation_kind == winapp::Activation::ActivationKind_Search)
    619     HandleSearchRequest(args);
    620   else if (activation_kind == winapp::Activation::ActivationKind_Protocol)
    621     HandleProtocolRequest(args);
    622   // We call ICoreWindow::Activate after the handling for the search/protocol
    623   // requests because Chrome can be launched to handle a search request which
    624   // in turn launches the chrome browser process in desktop mode via
    625   // ShellExecute. If we call ICoreWindow::Activate before this, then
    626   // Windows kills the metro chrome process when it calls ShellExecute. Seems
    627   // to be a bug.
    628   window_->Activate();
    629   return S_OK;
    630 }
    631 
    632 HRESULT ChromeAppViewAsh::OnPointerMoved(winui::Core::ICoreWindow* sender,
    633                                          winui::Core::IPointerEventArgs* args) {
    634   PointerInfoHandler pointer;
    635   HRESULT hr = pointer.Init(args);
    636   if (FAILED(hr))
    637     return hr;
    638 
    639   if (pointer.IsMouse()) {
    640     ui_channel_->Send(new MetroViewerHostMsg_MouseMoved(pointer.x(),
    641                                                         pointer.y(),
    642                                                         mouse_down_flags_));
    643   } else {
    644     DCHECK(pointer.IsTouch());
    645     ui_channel_->Send(new MetroViewerHostMsg_TouchMoved(pointer.x(),
    646                                                         pointer.y(),
    647                                                         pointer.timestamp(),
    648                                                         pointer.pointer_id()));
    649   }
    650   return S_OK;
    651 }
    652 
    653 // NOTE: From experimentation, it seems like Metro only sends a PointerPressed
    654 // event for the first button pressed and the last button released in a sequence
    655 // of mouse events.
    656 // For example, a sequence of LEFT_DOWN, RIGHT_DOWN, LEFT_UP, RIGHT_UP results
    657 // only in PointerPressed(LEFT)/PointerReleased(RIGHT) events.
    658 HRESULT ChromeAppViewAsh::OnPointerPressed(
    659     winui::Core::ICoreWindow* sender,
    660     winui::Core::IPointerEventArgs* args) {
    661   PointerInfoHandler pointer;
    662   HRESULT hr = pointer.Init(args);
    663   if (FAILED(hr))
    664     return hr;
    665 
    666   if (pointer.IsMouse()) {
    667     mouse_down_flags_ = pointer.flags();
    668     ui_channel_->Send(new MetroViewerHostMsg_MouseButton(pointer.x(),
    669                                                          pointer.y(),
    670                                                          0,
    671                                                          ui::ET_MOUSE_PRESSED,
    672                                                          mouse_down_flags_));
    673   } else {
    674     DCHECK(pointer.IsTouch());
    675     ui_channel_->Send(new MetroViewerHostMsg_TouchDown(pointer.x(),
    676                                                        pointer.y(),
    677                                                        pointer.timestamp(),
    678                                                        pointer.pointer_id()));
    679   }
    680   return S_OK;
    681 }
    682 
    683 HRESULT ChromeAppViewAsh::OnPointerReleased(
    684     winui::Core::ICoreWindow* sender,
    685     winui::Core::IPointerEventArgs* args) {
    686   PointerInfoHandler pointer;
    687   HRESULT hr = pointer.Init(args);
    688   if (FAILED(hr))
    689     return hr;
    690 
    691   if (pointer.IsMouse()) {
    692     mouse_down_flags_ = ui::EF_NONE;
    693     ui_channel_->Send(new MetroViewerHostMsg_MouseButton(pointer.x(),
    694                                                          pointer.y(),
    695                                                          0,
    696                                                          ui::ET_MOUSE_RELEASED,
    697                                                          pointer.flags()));
    698   } else {
    699     DCHECK(pointer.IsTouch());
    700     ui_channel_->Send(new MetroViewerHostMsg_TouchUp(pointer.x(),
    701                                                      pointer.y(),
    702                                                      pointer.timestamp(),
    703                                                      pointer.pointer_id()));
    704   }
    705   return S_OK;
    706 }
    707 
    708 HRESULT ChromeAppViewAsh::OnWheel(
    709     winui::Core::ICoreWindow* sender,
    710     winui::Core::IPointerEventArgs* args) {
    711   PointerInfoHandler pointer;
    712   HRESULT hr = pointer.Init(args);
    713   if (FAILED(hr))
    714     return hr;
    715   DCHECK(pointer.IsMouse());
    716   ui_channel_->Send(new MetroViewerHostMsg_MouseButton(pointer.x(), pointer.y(),
    717                                                        pointer.wheel_delta(),
    718                                                        ui::ET_MOUSEWHEEL,
    719                                                        ui::EF_NONE));
    720   return S_OK;
    721 }
    722 
    723 HRESULT ChromeAppViewAsh::OnKeyDown(
    724     winui::Core::ICoreWindow* sender,
    725     winui::Core::IKeyEventArgs* args) {
    726   winsys::VirtualKey virtual_key;
    727   HRESULT hr = args->get_VirtualKey(&virtual_key);
    728   if (FAILED(hr))
    729     return hr;
    730   winui::Core::CorePhysicalKeyStatus status;
    731   hr = args->get_KeyStatus(&status);
    732   if (FAILED(hr))
    733     return hr;
    734 
    735   ui_channel_->Send(new MetroViewerHostMsg_KeyDown(virtual_key,
    736                                                    status.RepeatCount,
    737                                                    status.ScanCode,
    738                                                    GetKeyboardEventFlags()));
    739   return S_OK;
    740 }
    741 
    742 HRESULT ChromeAppViewAsh::OnKeyUp(
    743     winui::Core::ICoreWindow* sender,
    744     winui::Core::IKeyEventArgs* args) {
    745   winsys::VirtualKey virtual_key;
    746   HRESULT hr = args->get_VirtualKey(&virtual_key);
    747   if (FAILED(hr))
    748     return hr;
    749   winui::Core::CorePhysicalKeyStatus status;
    750   hr = args->get_KeyStatus(&status);
    751   if (FAILED(hr))
    752     return hr;
    753 
    754   ui_channel_->Send(new MetroViewerHostMsg_KeyUp(virtual_key,
    755                                                  status.RepeatCount,
    756                                                  status.ScanCode,
    757                                                  GetKeyboardEventFlags()));
    758   return S_OK;
    759 }
    760 
    761 HRESULT ChromeAppViewAsh::OnAcceleratorKeyDown(
    762     winui::Core::ICoreDispatcher* sender,
    763     winui::Core::IAcceleratorKeyEventArgs* args) {
    764   winsys::VirtualKey virtual_key;
    765   HRESULT hr = args->get_VirtualKey(&virtual_key);
    766   if (FAILED(hr))
    767     return hr;
    768   winui::Core::CorePhysicalKeyStatus status;
    769   hr = args->get_KeyStatus(&status);
    770   if (FAILED(hr))
    771     return hr;
    772 
    773   winui::Core::CoreAcceleratorKeyEventType event_type;
    774   hr = args->get_EventType(&event_type);
    775   if (FAILED(hr))
    776     return hr;
    777 
    778   uint32 keyboard_flags = GetKeyboardEventFlags();
    779 
    780   switch (event_type) {
    781     case winui::Core::CoreAcceleratorKeyEventType_SystemCharacter:
    782       ui_channel_->Send(new MetroViewerHostMsg_Character(virtual_key,
    783                                                          status.RepeatCount,
    784                                                          status.ScanCode,
    785                                                          keyboard_flags));
    786       break;
    787 
    788     case winui::Core::CoreAcceleratorKeyEventType_SystemKeyDown:
    789       ui_channel_->Send(new MetroViewerHostMsg_KeyDown(virtual_key,
    790                                                        status.RepeatCount,
    791                                                        status.ScanCode,
    792                                                        keyboard_flags));
    793       break;
    794 
    795     case winui::Core::CoreAcceleratorKeyEventType_SystemKeyUp:
    796       ui_channel_->Send(new MetroViewerHostMsg_KeyUp(virtual_key,
    797                                                      status.RepeatCount,
    798                                                      status.ScanCode,
    799                                                      keyboard_flags));
    800       break;
    801 
    802     default:
    803       break;
    804   }
    805   return S_OK;
    806 }
    807 
    808 HRESULT ChromeAppViewAsh::OnCharacterReceived(
    809   winui::Core::ICoreWindow* sender,
    810   winui::Core::ICharacterReceivedEventArgs* args) {
    811   unsigned int char_code = 0;
    812   HRESULT hr = args->get_KeyCode(&char_code);
    813   if (FAILED(hr))
    814     return hr;
    815 
    816   winui::Core::CorePhysicalKeyStatus status;
    817   hr = args->get_KeyStatus(&status);
    818   if (FAILED(hr))
    819     return hr;
    820 
    821   ui_channel_->Send(new MetroViewerHostMsg_Character(char_code,
    822                                                      status.RepeatCount,
    823                                                      status.ScanCode,
    824                                                      GetKeyboardEventFlags()));
    825   return S_OK;
    826 }
    827 
    828 HRESULT ChromeAppViewAsh::OnVisibilityChanged(
    829     winui::Core::ICoreWindow* sender,
    830     winui::Core::IVisibilityChangedEventArgs* args) {
    831   boolean visible = false;
    832   HRESULT hr = args->get_Visible(&visible);
    833   if (FAILED(hr))
    834     return hr;
    835 
    836   ui_channel_->Send(new MetroViewerHostMsg_VisibilityChanged(!!visible));
    837   return S_OK;
    838 }
    839 
    840 HRESULT ChromeAppViewAsh::OnWindowActivated(
    841     winui::Core::ICoreWindow* sender,
    842     winui::Core::IWindowActivatedEventArgs* args) {
    843   winui::Core::CoreWindowActivationState state;
    844   HRESULT hr = args->get_WindowActivationState(&state);
    845   if (FAILED(hr))
    846     return hr;
    847   DVLOG(1) << "Window activation state: "  << state;
    848   ui_channel_->Send(new MetroViewerHostMsg_WindowActivated(
    849       state != winui::Core::CoreWindowActivationState_Deactivated));
    850   return S_OK;
    851 }
    852 
    853 HRESULT ChromeAppViewAsh::HandleSearchRequest(
    854     winapp::Activation::IActivatedEventArgs* args) {
    855   mswr::ComPtr<winapp::Activation::ISearchActivatedEventArgs> search_args;
    856   CheckHR(args->QueryInterface(
    857           winapp::Activation::IID_ISearchActivatedEventArgs, &search_args));
    858 
    859   if (!ui_channel_) {
    860     DVLOG(1) << "Launched to handle search request";
    861     base::FilePath chrome_exe_path;
    862 
    863     if (!PathService::Get(base::FILE_EXE, &chrome_exe_path))
    864       return E_FAIL;
    865 
    866     SHELLEXECUTEINFO sei = { sizeof(sei) };
    867     sei.nShow = SW_SHOWNORMAL;
    868     sei.lpFile = chrome_exe_path.value().c_str();
    869     sei.lpDirectory = L"";
    870     sei.lpParameters =
    871         L"--silent-launch --viewer-connection=viewer --windows8-search";
    872     ::ShellExecuteEx(&sei);
    873   }
    874 
    875   mswrw::HString search_string;
    876   CheckHR(search_args->get_QueryText(search_string.GetAddressOf()));
    877   string16 search_text(MakeStdWString(search_string.Get()));
    878 
    879   ui_loop_.PostTask(FROM_HERE,
    880                     base::Bind(&ChromeAppViewAsh::OnSearchRequest,
    881                     base::Unretained(this),
    882                     search_text));
    883   return S_OK;
    884 }
    885 
    886 HRESULT ChromeAppViewAsh::HandleProtocolRequest(
    887     winapp::Activation::IActivatedEventArgs* args) {
    888   DVLOG(1) << __FUNCTION__;
    889   if (!ui_channel_)
    890     DVLOG(1) << "Launched to handle url request";
    891 
    892   mswr::ComPtr<winapp::Activation::IProtocolActivatedEventArgs>
    893       protocol_args;
    894   CheckHR(args->QueryInterface(
    895           winapp::Activation::IID_IProtocolActivatedEventArgs,
    896           &protocol_args));
    897 
    898   mswr::ComPtr<winfoundtn::IUriRuntimeClass> uri;
    899   protocol_args->get_Uri(&uri);
    900   mswrw::HString url;
    901   uri->get_AbsoluteUri(url.GetAddressOf());
    902   string16 actual_url(MakeStdWString(url.Get()));
    903   DVLOG(1) << "Received url request: " << actual_url;
    904 
    905   ui_loop_.PostTask(FROM_HERE,
    906                     base::Bind(&ChromeAppViewAsh::OnNavigateToUrl,
    907                                base::Unretained(this),
    908                                actual_url));
    909   return S_OK;
    910 }
    911 
    912 void ChromeAppViewAsh::OnSearchRequest(const string16& search_string) {
    913   DCHECK(ui_channel_);
    914   ui_channel_->Send(new MetroViewerHostMsg_SearchRequest(search_string));
    915 }
    916 
    917 void ChromeAppViewAsh::OnNavigateToUrl(const string16& url) {
    918   DCHECK(ui_channel_);
    919  ui_channel_->Send(new MetroViewerHostMsg_OpenURL(url));
    920 }
    921 
    922 HRESULT ChromeAppViewAsh::OnSizeChanged(winui::Core::ICoreWindow* sender,
    923     winui::Core::IWindowSizeChangedEventArgs* args) {
    924   if (!window_) {
    925     return S_OK;
    926   }
    927 
    928   winfoundtn::Size size;
    929   HRESULT hr = args->get_Size(&size);
    930   if (FAILED(hr))
    931     return hr;
    932 
    933   uint32 cx = static_cast<uint32>(size.Width);
    934   uint32 cy = static_cast<uint32>(size.Height);
    935 
    936   DVLOG(1) << "Window size changed: width=" << cx << ", height=" << cy;
    937   ui_channel_->Send(new MetroViewerHostMsg_WindowSizeChanged(cx, cy));
    938   return S_OK;
    939 }
    940 
    941 ///////////////////////////////////////////////////////////////////////////////
    942 
    943 ChromeAppViewFactory::ChromeAppViewFactory(
    944     winapp::Core::ICoreApplication* icore_app,
    945     LPTHREAD_START_ROUTINE host_main,
    946     void* host_context) {
    947   mswr::ComPtr<winapp::Core::ICoreApplication> core_app(icore_app);
    948   mswr::ComPtr<winapp::Core::ICoreApplicationExit> app_exit;
    949   CheckHR(core_app.As(&app_exit));
    950   globals.app_exit = app_exit.Detach();
    951 }
    952 
    953 IFACEMETHODIMP
    954 ChromeAppViewFactory::CreateView(winapp::Core::IFrameworkView** view) {
    955   *view = mswr::Make<ChromeAppViewAsh>().Detach();
    956   return (*view) ? S_OK :  E_OUTOFMEMORY;
    957 }
    958