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